# Motivation

Dynamic configuration is an important feature in the production environment, the Pulsar supports this feature,
see `ServiceConfiguration.java`, when the field has `@FieldContext(dynamic = true)` means it is a dynamic configuration,
and then we can use the `pulsar-admin` to update this field.

The Pulsar has multiple pluggable plugins like authentication provider, authorization provider, and so on. In the
plugin, we can get the Pulsar configurations and customized configurations, in some scenarios, we need to use
the `pulsar-admin` to change the values of customized configuration, but the Pulsar doesn't support this operation.

# Goals

## In Scope

The goal of this PIP is to allow the `pulsar-admin` to update the values of customized configuration, and also use the
listener to listen the customized configuration changes
by `org.apache.pulsar.broker.service.BrokerService.registerConfigurationListener`.

# Detailed Design

## Design & Implementation Details

The `org.apache.pulsar.broker.service.BrokerService.dynamicConfigurationMap` holds the dynamic configurations, so we
just to add the configurations that need to be dynamically configured to this map, and then we can use
the `pulsar-admin` to update the values of these configurations.

Users can only register custom configurations and only once.  If multiple registrations are performed, an exception will be thrown.

Add `void registerCustomDynamicConfiguration(String key, Predicate<String> validator)` to the `BrokerService.java`, 

```java
public void registerCustomDynamicConfiguration(String key, Predicate<String> validator) {
    if (dynamicConfigurationMap.containsKey(key)) {
        throw new IllegalArgumentException(key + " already exists in the dynamicConfigurationMap");
    }

    ConfigField configField = ConfigField.newCustomConfigField(null);
    configField.validator = validator;
    dynamicConfigurationMap.put(key, configField);
}
```

Example of using this feature in the plugin:

```java
@Override
public void initialize(PulsarService pulsarService) throws Exception {
    String myAuthIdKey = "my-auth-id";
    myAuthIdValue = pulsarService.getConfiguration().getProperties().getProperty(myAuthIdKey);

    pulsarService.getBrokerService().registerCustomDynamicConfiguration(myAuthIdKey, null);
    pulsarService.getBrokerService().registerConfigurationListener(myAuthIdKey, (newValue) -> {
        // The `myAuthIdKey` value has changed
        myAuthIdValue = String.valueOf(newValue);
    });
}
```


# Backward & Forward Compatibility

## Revert

# General Notes

# Links

* Mailing List discussion thread: https://lists.apache.org/thread/ysnsnollgy1b6w1dsvmx1t1y2rz1tyd6
* Mailing List voting thread: https://lists.apache.org/thread/cp4zdlz60md775ptzxbbj8bs2n2osfjb
* PR: https://github.com/apache/pulsar/pull/20884
