| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package config |
| |
| import ( |
| "context" |
| "reflect" |
| "strconv" |
| "strings" |
| ) |
| |
| import ( |
| perrors "github.com/pkg/errors" |
| ) |
| |
| import ( |
| "github.com/apache/dubbo-go/common" |
| "github.com/apache/dubbo-go/common/config" |
| "github.com/apache/dubbo-go/common/extension" |
| "github.com/apache/dubbo-go/common/logger" |
| "github.com/apache/dubbo-go/config_center" |
| ) |
| |
| type multiConfiger interface { |
| Prefix() string |
| } |
| |
| type BaseConfig struct { |
| ConfigCenterConfig *ConfigCenterConfig `yaml:"config_center" json:"config_center,omitempty"` |
| configCenterUrl *common.URL |
| prefix string |
| fatherConfig interface{} |
| } |
| |
| func (c *BaseConfig) startConfigCenter(ctx context.Context) error { |
| url, err := common.NewURL(ctx, c.ConfigCenterConfig.Address, common.WithProtocol(c.ConfigCenterConfig.Protocol), common.WithParams(c.ConfigCenterConfig.GetUrlMap())) |
| if err != nil { |
| return err |
| } |
| c.configCenterUrl = &url |
| if c.prepareEnvironment() != nil { |
| return perrors.WithMessagef(err, "start config center error!") |
| } |
| //c.fresh() |
| return err |
| } |
| |
| func (c *BaseConfig) prepareEnvironment() error { |
| |
| factory := extension.GetConfigCenterFactory(c.ConfigCenterConfig.Protocol) |
| dynamicConfig, err := factory.GetDynamicConfiguration(c.configCenterUrl) |
| config.GetEnvInstance().SetDynamicConfiguration(dynamicConfig) |
| if err != nil { |
| logger.Errorf("Get dynamic configuration error , error message is %v", err) |
| return perrors.WithStack(err) |
| } |
| content, err := dynamicConfig.GetProperties(c.ConfigCenterConfig.ConfigFile, config_center.WithGroup(c.ConfigCenterConfig.Group)) |
| if err != nil { |
| logger.Errorf("Get config content in dynamic configuration error , error message is %v", err) |
| return perrors.WithStack(err) |
| } |
| var appGroup string |
| var appContent string |
| if providerConfig != nil && providerConfig.ApplicationConfig != nil && |
| reflect.ValueOf(c.fatherConfig).Elem().Type().Name() == "ProviderConfig" { |
| appGroup = providerConfig.ApplicationConfig.Name |
| } else if consumerConfig != nil && consumerConfig.ApplicationConfig != nil && |
| reflect.ValueOf(c.fatherConfig).Elem().Type().Name() == "ConsumerConfig" { |
| appGroup = consumerConfig.ApplicationConfig.Name |
| } |
| |
| if len(appGroup) != 0 { |
| configFile := c.ConfigCenterConfig.AppConfigFile |
| if len(configFile) == 0 { |
| configFile = c.ConfigCenterConfig.ConfigFile |
| } |
| appContent, err = dynamicConfig.GetProperties(configFile, config_center.WithGroup(appGroup)) |
| } |
| //global config file |
| mapContent, err := dynamicConfig.Parser().Parse(content) |
| if err != nil { |
| return perrors.WithStack(err) |
| } |
| config.GetEnvInstance().UpdateExternalConfigMap(mapContent) |
| |
| //appGroup config file |
| if len(appContent) != 0 { |
| appMapConent, err := dynamicConfig.Parser().Parse(appContent) |
| if err != nil { |
| return perrors.WithStack(err) |
| } |
| config.GetEnvInstance().UpdateAppExternalConfigMap(appMapConent) |
| } |
| |
| return nil |
| } |
| |
| func getKeyPrefix(val reflect.Value) []string { |
| var ( |
| prefix string |
| ) |
| |
| if val.CanAddr() { |
| prefix = val.Addr().MethodByName("Prefix").Call(nil)[0].String() |
| } else { |
| prefix = val.MethodByName("Prefix").Call(nil)[0].String() |
| } |
| var retPrefixs []string |
| |
| for _, pfx := range strings.Split(prefix, "|") { |
| |
| retPrefixs = append(retPrefixs, pfx) |
| |
| } |
| return retPrefixs |
| |
| } |
| func getPtrElement(v reflect.Value) reflect.Value { |
| if v.Kind() == reflect.Ptr { |
| v = v.Elem() |
| if v.Kind() == reflect.Ptr { |
| return getPtrElement(v) |
| } |
| } |
| return v |
| } |
| func setFieldValue(val reflect.Value, id reflect.Value, config *config.InmemoryConfiguration) { |
| for i := 0; i < val.NumField(); i++ { |
| if key := val.Type().Field(i).Tag.Get("property"); key != "-" && key != "" { |
| f := val.Field(i) |
| if f.IsValid() { |
| setBaseValue := func(f reflect.Value) { |
| |
| var ( |
| ok bool |
| value string |
| idStr string |
| ) |
| |
| prefixs := getKeyPrefix(val) |
| |
| if id.Kind() == reflect.String { |
| idStr = id.Interface().(string) |
| } |
| |
| for _, pfx := range prefixs { |
| |
| if len(pfx) > 0 { |
| if len(idStr) > 0 { |
| ok, value = config.GetProperty(pfx + idStr + "." + key) |
| } |
| if len(value) == 0 || !ok { |
| ok, value = config.GetProperty(pfx + key) |
| } |
| |
| } else { |
| ok, value = config.GetProperty(key) |
| } |
| |
| if ok { |
| break |
| } |
| |
| } |
| if ok { |
| switch f.Kind() { |
| case reflect.Int64: |
| x, err := strconv.Atoi(value) |
| if err != nil { |
| logger.Errorf("Dynamic change the configuration in struct {%v} field {%v} error ,error message is {%v}", |
| val.Type().Name(), val.Type().Field(i).Name, err) |
| } else { |
| if !f.OverflowInt(int64(x)) { |
| f.SetInt(int64(x)) |
| } else { |
| logger.Errorf("Dynamic change the configuration in struct {%v} field {%v} error ,error message is {%v}", |
| val.Type().Name(), val.Type().Field(i).Name, perrors.Errorf("the int64 value {%v} from config center is overflow", int64(x))) |
| } |
| } |
| case reflect.String: |
| f.SetString(value) |
| case reflect.Bool: |
| x, err := strconv.ParseBool(value) |
| if err != nil { |
| logger.Errorf("Dynamic change the configuration in struct {%v} field {%v} error ,error message is {%v}", |
| val.Type().Name(), val.Type().Field(i).Name, err) |
| } |
| f.SetBool(x) |
| case reflect.Float64: |
| x, err := strconv.ParseFloat(value, 64) |
| if err != nil { |
| logger.Errorf("Dynamic change the configuration in struct {%v} field {%v} error ,error message is {%v}", |
| val.Type().Name(), val.Type().Field(i).Name, err) |
| } else { |
| if !f.OverflowFloat(x) { |
| f.SetFloat(x) |
| } else { |
| logger.Errorf("Dynamic change the configuration in struct {%v} field {%v} error ,error message is {%v}", |
| val.Type().Name(), val.Type().Field(i).Name, perrors.Errorf("the float64 value {%v} from config center is overflow", x)) |
| } |
| } |
| default: |
| logger.Warnf("The kind of field {%v} is not supported ", f.Kind().String()) |
| } |
| |
| } |
| |
| } |
| |
| if f.Kind() == reflect.Ptr { |
| f = getPtrElement(f) |
| if f.Kind() == reflect.Struct { |
| setFieldValue(f, reflect.Value{}, config) |
| } else { |
| setBaseValue(f) |
| } |
| } |
| |
| if f.Kind() == reflect.Struct { |
| setFieldValue(f, reflect.Value{}, config) |
| } |
| if f.Kind() == reflect.Slice { |
| for i := 0; i < f.Len(); i++ { |
| e := f.Index(i) |
| if e.Kind() == reflect.Ptr { |
| e = getPtrElement(e) |
| if e.Kind() == reflect.Struct { |
| setFieldValue(e, reflect.Value{}, config) |
| } else { |
| setBaseValue(e) |
| } |
| } |
| |
| } |
| |
| } |
| if f.Kind() == reflect.Map { |
| |
| if f.Type().Elem().Kind() == reflect.Ptr { |
| //initiate config |
| s := reflect.New(f.Type().Elem().Elem()) |
| prefix := s.MethodByName("Prefix").Call(nil)[0].String() |
| for _, pfx := range strings.Split(prefix, "|") { |
| m := config.GetSubProperty(pfx) |
| if m != nil { |
| for k := range m { |
| f.SetMapIndex(reflect.ValueOf(k), reflect.New(f.Type().Elem().Elem())) |
| } |
| } |
| |
| } |
| |
| } |
| |
| //iter := f.MapRange() |
| |
| for _, k := range f.MapKeys() { |
| v := f.MapIndex(k) |
| switch v.Kind() { |
| case reflect.Ptr: |
| v = getPtrElement(v) |
| if v.Kind() == reflect.Struct { |
| setFieldValue(v, k, config) |
| } else { |
| setBaseValue(v) |
| } |
| case reflect.Int64, reflect.String, reflect.Bool, reflect.Float64: |
| setBaseValue(v) |
| default: |
| logger.Warnf("The kind of field {%v} is not supported ", v.Kind().String()) |
| } |
| } |
| } |
| setBaseValue(f) |
| |
| } |
| } |
| } |
| } |
| func (c *BaseConfig) fresh() { |
| configList := config.GetEnvInstance().Configuration() |
| for element := configList.Front(); element != nil; element = element.Next() { |
| config := element.Value.(*config.InmemoryConfiguration) |
| c.freshInternalConfig(config) |
| } |
| } |
| |
| func (c *BaseConfig) freshInternalConfig(config *config.InmemoryConfiguration) { |
| //reflect to init struct |
| tp := reflect.ValueOf(c.fatherConfig).Elem().Type() |
| initializeStruct(tp, reflect.ValueOf(c.fatherConfig).Elem()) |
| |
| val := reflect.Indirect(reflect.ValueOf(c.fatherConfig)) |
| setFieldValue(val, reflect.Value{}, config) |
| } |
| |
| func (c *BaseConfig) SetFatherConfig(fatherConfig interface{}) { |
| c.fatherConfig = fatherConfig |
| } |
| |
| func initializeStruct(t reflect.Type, v reflect.Value) { |
| if v.Kind() == reflect.Struct { |
| for i := 0; i < v.NumField(); i++ { |
| f := v.Field(i) |
| ft := t.Field(i) |
| |
| if ft.Tag.Get("property") != "" { |
| switch ft.Type.Kind() { |
| case reflect.Map: |
| if f.IsNil() { |
| f.Set(reflect.MakeMap(ft.Type)) |
| } |
| case reflect.Slice: |
| if f.IsNil() { |
| f.Set(reflect.MakeSlice(ft.Type, 0, 0)) |
| } |
| case reflect.Chan: |
| if f.IsNil() { |
| f.Set(reflect.MakeChan(ft.Type, 0)) |
| } |
| case reflect.Struct: |
| if f.IsNil() { |
| initializeStruct(ft.Type, f) |
| } |
| case reflect.Ptr: |
| if f.IsNil() { |
| fv := reflect.New(ft.Type.Elem()) |
| initializeStruct(ft.Type.Elem(), fv.Elem()) |
| f.Set(fv) |
| } |
| default: |
| } |
| } |
| |
| } |
| } |
| |
| } |