55
66package io .opentelemetry .instrumentation .api .incubator .config ;
77
8-
98import java .util .List ;
109import java .util .Map ;
1110import java .util .concurrent .ConcurrentHashMap ;
1615import java .util .logging .Logger ;
1716import javax .annotation .Nullable ;
1817
19- // MutableConfigProvider: If MutableConfigProvider was supported, this registry could be enhanced to:
20- // - Register as a listener with the MutableConfigProvider to receive immediate notifications on config changes
21- // - Eliminate the need for periodic polling (executor) and instead react to provider-driven change events
18+ // MutableConfigProvider: If MutableConfigProvider was supported, this registry could be enhanced
19+ // to:
20+ // - Register as a listener with the MutableConfigProvider to receive immediate notifications on
21+ // config changes
22+ // - Eliminate the need for periodic polling (executor) and instead react to provider-driven change
23+ // events
2224// - Support dynamic config updates without the 30-second polling delay
2325
2426/**
2527 * Registry for callbacks that are invoked when configuration option values change.
2628 *
27- * <p>This singleton can be loaded by multiple classloaders, creating separate instances that each monitor
28- * the globally consistent System properties. For instances that don't need periodic checking (e.g., only
29- * used for {@link #updateOption(String, String)}), {@link #shutdownPeriodicChecker()} can be called
30- * to stop the background thread. Typically this would mean the extension that loads this class to use
31- * {@link #updateOption(String, String)}) and no other capability of this class, should shutdown the
32- * periodic checker during the extension initialization task. If this is not done, there are no adverse
33- * effects other than the additional very low overhead thread.
29+ * <p>This singleton can be loaded by multiple classloaders, creating separate instances that each
30+ * monitor the globally consistent System properties. For instances that don't need periodic
31+ * checking (e.g., only used for {@link #updateOption(String, String)}), {@link
32+ * #shutdownPeriodicChecker()} can be called to stop the background thread. Typically this would
33+ * mean the extension that loads this class to use {@link #updateOption(String, String)}) and no
34+ * other capability of this class, should shutdown the periodic checker during the extension
35+ * initialization task. If this is not done, there are no adverse effects other than the additional
36+ * very low overhead thread.
3437 *
35- * <p>This class is internal and is hence not for public use. Its APIs are unstable and can change at
36- * any time.
38+ * <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
39+ * at any time.
3740 */
3841public final class OptionCallbackRegistry {
3942
@@ -47,8 +50,10 @@ public final class OptionCallbackRegistry {
4750 // Keep previous values so that only changes get notified
4851 private final Map <String , String > previousValues = new ConcurrentHashMap <>();
4952
50- // MutableConfigProvider: This executor could be unnecessary if MutableConfigProvider was available,
51- // as we could register directly with the provider for immediate change notifications instead of polling
53+ // MutableConfigProvider: This executor could be unnecessary if MutableConfigProvider was
54+ // available,
55+ // as we could register directly with the provider for immediate change notifications instead of
56+ // polling
5257 private final ScheduledExecutorService executor =
5358 Executors .newSingleThreadScheduledExecutor (
5459 r -> {
@@ -75,23 +80,28 @@ public void shutdownPeriodicChecker() {
7580 }
7681
7782 /**
78- * Registers a listener to be invoked when the value of the specified option key changes.
79- *
80- * <p>The listener receives the key and the new value. If the value becomes null, the listener is
81- * still invoked.
82- *
83- * <p>MutableConfigProvider: With MutableConfigProvider, this method would register the listener
84- * directly with the provider for the specific key, ensuring immediate notification of changes
85- * without relying on polling.
86- *
87- * @param key the configuration key to monitor
88- * @param currentValue the current value of the key
89- * @param listener the listener to invoke on change
90- * @param isDeclarative if true, the currentValue is passed into {@link #updateOption(String, String)}. Where the config is declarative, this should be true, otherwise this should be false
91- */
92- public void registerCallback (String key , String currentValue , OptionChangeListener listener , boolean isDeclarative ) {
93- // If instrumentations could be unloaded, we would wrap listeners in a WeakReference to prevent memory leaks:
94- // callbacks.computeIfAbsent(key, k -> new CopyOnWriteArrayList<>()).add(new WeakReference<>(listener));
83+ * Registers a listener to be invoked when the value of the specified option key changes.
84+ *
85+ * <p>The listener receives the key and the new value. If the value becomes null, the listener is
86+ * still invoked.
87+ *
88+ * <p>MutableConfigProvider: With MutableConfigProvider, this method would register the listener
89+ * directly with the provider for the specific key, ensuring immediate notification of changes
90+ * without relying on polling.
91+ *
92+ * @param key the configuration key to monitor
93+ * @param currentValue the current value of the key
94+ * @param listener the listener to invoke on change
95+ * @param isDeclarative if true, the currentValue is passed into {@link #updateOption(String,
96+ * String)}. Where the config is declarative, this should be true, otherwise this should be
97+ * false
98+ */
99+ public void registerCallback (
100+ String key , String currentValue , OptionChangeListener listener , boolean isDeclarative ) {
101+ // If instrumentations could be unloaded, we would wrap listeners in a WeakReference to prevent
102+ // memory leaks:
103+ // callbacks.computeIfAbsent(key, k -> new CopyOnWriteArrayList<>()).add(new
104+ // WeakReference<>(listener));
95105
96106 callbacks .computeIfAbsent (key , k -> new CopyOnWriteArrayList <>()).add (listener );
97107 if (isDeclarative ) {
@@ -101,30 +111,32 @@ public void registerCallback(String key, String currentValue, OptionChangeListen
101111 }
102112
103113 /**
104- * Updates the value of an option. Registered callbacks will be notified asynchronously
105- * when the periodic check detects the change.
106- *
107- * <p>MutableConfigProvider: If MutableConfigProvider was available, this method would be
108- * replaced by provider-driven updates. The MutableConfigProvider could notify this registry
109- * directly when configurations change, eliminating the need for manual updates via this method.
110- * Alternatively or additionally, this method could remain the primary runtime update mechanism,
111- * handling the required updates to MutableConfigProvider.
112- *
113- * @param key the option key
114- * @param value the new value, or {@code null} to remove the option
115- */
114+ * Updates the value of an option. Registered callbacks will be notified asynchronously when the
115+ * periodic check detects the change.
116+ *
117+ * <p>MutableConfigProvider: If MutableConfigProvider was available, this method would be replaced
118+ * by provider-driven updates. The MutableConfigProvider could notify this registry directly when
119+ * configurations change, eliminating the need for manual updates via this method. Alternatively
120+ * or additionally, this method could remain the primary runtime update mechanism, handling the
121+ * required updates to MutableConfigProvider.
122+ *
123+ * @param key the option key
124+ * @param value the new value, or {@code null} to remove the option
125+ */
116126 public void updateOption (String key , String value ) {
117127 if (value != null ) {
118128 // System.setProperty is thread-safe and provides consistent writes so no need to synchronize
119129 System .setProperty (key , value );
120130 } else {
121131 // Remove the property if value is null
122- // System.clearProperty is thread-safe and provides consistent writes so no need to synchronize
132+ // System.clearProperty is thread-safe and provides consistent writes so no need to
133+ // synchronize
123134 System .clearProperty (key );
124135 }
125136 }
126137
127- // MutableConfigProvider: This polling method could be replaced by direct callback from MutableConfigProvider.
138+ // MutableConfigProvider: This polling method could be replaced by direct callback from
139+ // MutableConfigProvider.
128140 // The provider could call a method like onConfigChanged(String key, String newValue) directly,
129141 // eliminating the need to check all keys periodically and providing immediate updates.
130142 private void checkForChanges () {
@@ -152,7 +164,11 @@ private void notifyCallbacks(String key, @Nullable String newValue, @Nullable St
152164 try {
153165 listener .onOptionChanged (key , newValue , oldValue );
154166 } catch (Throwable t ) {
155- logger .info ("Warning, exception thrown when trying to notify listener for key '" + key + "': " + t .getMessage ());
167+ logger .info (
168+ "Warning, exception thrown when trying to notify listener for key '"
169+ + key
170+ + "': "
171+ + t .getMessage ());
156172 }
157173 }
158174 }
0 commit comments