Skip to content

Commit cbaeb85

Browse files
Fix: Added notificationRegistry to make sure that odpSettings updates (#501)
* Added notificationRegistry to make sure that odpSettings update will call upon UpdateConfig * Updated headers and added additional checks in unittests * Added suppress warning * renamed NoficationRegistry function. Added default implementation of getSDKKey in ProjectConfigManager * reverting header change * reverting extra changes * reverting extra changes * nit fix * nit * Resolved comments * Added getCachedconfig Function to return ProjectConfig without wait, but the default implementation of it will return null. * ProjectConfig getCachedConfig(); without default implementation * Removed default implementation of getSDKkey to enforce user to make their own choice. * Resolved comments * setting Sdk key inside pollingConfigManager to make sure that the sdkKey for a particular config is always same. * setting sdkKey in parent class * fix * making sure that if the SDKKey is set by the user then it should be picked from projectConfig * Refactored and moved getSdkKey to pollingConfigManager to make sure that to trigger the notification always user provided sdkKey will be prioritized Co-authored-by: mnoman09 <m.nomanshoaib09@gmail.com>
1 parent cbe85de commit cbaeb85

File tree

12 files changed

+306
-46
lines changed

12 files changed

+306
-46
lines changed

core-api/src/main/java/com/optimizely/ab/Optimizely.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import com.optimizely.ab.event.*;
2828
import com.optimizely.ab.event.internal.*;
2929
import com.optimizely.ab.event.internal.payload.EventBatch;
30+
import com.optimizely.ab.internal.NotificationRegistry;
3031
import com.optimizely.ab.notification.*;
3132
import com.optimizely.ab.odp.*;
3233
import com.optimizely.ab.optimizelyconfig.OptimizelyConfig;
@@ -124,10 +125,15 @@ private Optimizely(@Nonnull EventHandler eventHandler,
124125

125126
if (odpManager != null) {
126127
odpManager.getEventManager().start();
127-
if (getProjectConfig() != null) {
128+
if (projectConfigManager.getCachedConfig() != null) {
128129
updateODPSettings();
129130
}
130-
addUpdateConfigNotificationHandler(configNotification -> { updateODPSettings(); });
131+
if (projectConfigManager.getSDKKey() != null) {
132+
NotificationRegistry.getInternalNotificationCenter(projectConfigManager.getSDKKey()).
133+
addNotificationHandler(UpdateConfigNotification.class,
134+
configNotification -> { updateODPSettings(); });
135+
}
136+
131137
}
132138
}
133139

@@ -153,6 +159,8 @@ public void close() {
153159
tryClose(eventProcessor);
154160
tryClose(eventHandler);
155161
tryClose(projectConfigManager);
162+
notificationCenter.clearAllNotificationListeners();
163+
NotificationRegistry.clearNotificationCenterRegistry(projectConfigManager.getSDKKey());
156164
if (odpManager != null) {
157165
tryClose(odpManager);
158166
}
@@ -1477,8 +1485,8 @@ public void identifyUser(@Nonnull String userId) {
14771485
}
14781486

14791487
private void updateODPSettings() {
1480-
if (odpManager != null && getProjectConfig() != null) {
1481-
ProjectConfig projectConfig = getProjectConfig();
1488+
ProjectConfig projectConfig = projectConfigManager.getCachedConfig();
1489+
if (odpManager != null && projectConfig != null) {
14821490
odpManager.updateSettings(projectConfig.getHostForODP(), projectConfig.getPublicKeyForODP(), projectConfig.getAllSegments());
14831491
}
14841492
}

core-api/src/main/java/com/optimizely/ab/config/AtomicProjectConfigManager.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
*
3-
* Copyright 2019, Optimizely and contributors
3+
* Copyright 2019, 2023, Optimizely and contributors
44
*
55
* Licensed under the Apache License, Version 2.0 (the "License");
66
* you may not use this file except in compliance with the License.
@@ -27,6 +27,21 @@ public ProjectConfig getConfig() {
2727
return projectConfigReference.get();
2828
}
2929

30+
/**
31+
* Access to current cached project configuration.
32+
*
33+
* @return {@link ProjectConfig}
34+
*/
35+
@Override
36+
public ProjectConfig getCachedConfig() {
37+
return projectConfigReference.get();
38+
}
39+
40+
@Override
41+
public String getSDKKey() {
42+
return null;
43+
}
44+
3045
public void setConfig(ProjectConfig projectConfig) {
3146
projectConfigReference.set(projectConfig);
3247
}

core-api/src/main/java/com/optimizely/ab/config/PollingProjectConfigManager.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
*
3-
* Copyright 2019-2020, Optimizely and contributors
3+
* Copyright 2019-2020, 2023, Optimizely and contributors
44
*
55
* Licensed under the Apache License, Version 2.0 (the "License");
66
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
1616
*/
1717
package com.optimizely.ab.config;
1818

19+
import com.optimizely.ab.internal.NotificationRegistry;
1920
import com.optimizely.ab.notification.NotificationCenter;
2021
import com.optimizely.ab.notification.UpdateConfigNotification;
2122
import com.optimizely.ab.optimizelyconfig.OptimizelyConfig;
@@ -56,6 +57,7 @@ public abstract class PollingProjectConfigManager implements ProjectConfigManage
5657

5758
private final CountDownLatch countDownLatch = new CountDownLatch(1);
5859

60+
private volatile String sdkKey;
5961
private volatile boolean started;
6062
private ScheduledFuture<?> scheduledFuture;
6163

@@ -84,6 +86,16 @@ public PollingProjectConfigManager(long period, TimeUnit timeUnit, long blocking
8486

8587
protected abstract ProjectConfig poll();
8688

89+
/**
90+
* Access to current cached project configuration, This is to make sure that config returns without any wait, even if it is null.
91+
*
92+
* @return {@link ProjectConfig}
93+
*/
94+
@Override
95+
public ProjectConfig getCachedConfig() {
96+
return currentProjectConfig.get();
97+
}
98+
8799
/**
88100
* Only allow the ProjectConfig to be set to a non-null value, if and only if the value has not already been set.
89101
* @param projectConfig
@@ -109,6 +121,13 @@ void setConfig(ProjectConfig projectConfig) {
109121
currentProjectConfig.set(projectConfig);
110122
currentOptimizelyConfig.set(new OptimizelyConfigService(projectConfig).getConfig());
111123
countDownLatch.countDown();
124+
125+
if (sdkKey == null) {
126+
sdkKey = projectConfig.getSdkKey();
127+
}
128+
if (sdkKey != null) {
129+
NotificationRegistry.getInternalNotificationCenter(sdkKey).send(SIGNAL);
130+
}
112131
notificationCenter.send(SIGNAL);
113132
}
114133

@@ -150,6 +169,11 @@ public OptimizelyConfig getOptimizelyConfig() {
150169
return currentOptimizelyConfig.get();
151170
}
152171

172+
@Override
173+
public String getSDKKey() {
174+
return this.sdkKey;
175+
}
176+
153177
public synchronized void start() {
154178
if (started) {
155179
logger.warn("Manager already started.");
@@ -189,6 +213,10 @@ public synchronized void close() {
189213
started = false;
190214
}
191215

216+
protected void setSdkKey(String sdkKey) {
217+
this.sdkKey = sdkKey;
218+
}
219+
192220
public boolean isRunning() {
193221
return started;
194222
}

core-api/src/main/java/com/optimizely/ab/config/ProjectConfigManager.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
*
3-
* Copyright 2019, Optimizely and contributors
3+
* Copyright 2019, 2023, Optimizely and contributors
44
*
55
* Licensed under the Apache License, Version 2.0 (the "License");
66
* you may not use this file except in compliance with the License.
@@ -16,12 +16,33 @@
1616
*/
1717
package com.optimizely.ab.config;
1818

19+
import javax.annotation.Nullable;
20+
1921
public interface ProjectConfigManager {
2022
/**
2123
* Implementations of this method should block until a datafile is available.
2224
*
2325
* @return ProjectConfig
2426
*/
2527
ProjectConfig getConfig();
28+
29+
/**
30+
* Implementations of this method should not block until a datafile is available, instead return current cached project configuration.
31+
* return null if ProjectConfig is not ready at the moment.
32+
*
33+
* NOTE: To use ODP segments, implementation of this function is required to return current project configuration.
34+
* @return ProjectConfig
35+
*/
36+
@Nullable
37+
ProjectConfig getCachedConfig();
38+
39+
/**
40+
* Implementations of this method should return SDK key. If there is no SDKKey then it should return null.
41+
*
42+
* NOTE: To update ODP segments configuration via polling, it is required to return sdkKey.
43+
* @return String
44+
*/
45+
@Nullable
46+
String getSDKKey();
2647
}
2748

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/**
2+
*
3+
* Copyright 2023, Optimizely
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package com.optimizely.ab.internal;
18+
19+
import com.optimizely.ab.notification.NotificationCenter;
20+
21+
import javax.annotation.Nonnull;
22+
import java.util.Map;
23+
import java.util.concurrent.ConcurrentHashMap;
24+
25+
public class NotificationRegistry {
26+
private final static Map<String, NotificationCenter> _notificationCenters = new ConcurrentHashMap<>();
27+
28+
private NotificationRegistry()
29+
{
30+
}
31+
32+
public static NotificationCenter getInternalNotificationCenter(@Nonnull String sdkKey)
33+
{
34+
NotificationCenter notificationCenter = null;
35+
if (sdkKey != null) {
36+
if (_notificationCenters.containsKey(sdkKey)) {
37+
notificationCenter = _notificationCenters.get(sdkKey);
38+
} else {
39+
notificationCenter = new NotificationCenter();
40+
_notificationCenters.put(sdkKey, notificationCenter);
41+
}
42+
}
43+
return notificationCenter;
44+
}
45+
46+
public static void clearNotificationCenterRegistry(@Nonnull String sdkKey) {
47+
if (sdkKey != null) {
48+
_notificationCenters.remove(sdkKey);
49+
}
50+
}
51+
52+
}

core-api/src/test/java/com/optimizely/ab/OptimizelyTest.java

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/****************************************************************************
2-
* Copyright 2016-2022, Optimizely, Inc. and contributors *
2+
* Copyright 2016-2023, Optimizely, Inc. and contributors *
33
* *
44
* Licensed under the Apache License, Version 2.0 (the "License"); *
55
* you may not use this file except in compliance with the License. *
@@ -119,6 +119,23 @@ public static Collection<Object[]> data() throws IOException {
119119
public OptimizelyRule optimizelyBuilder = new OptimizelyRule();
120120
public EventHandlerRule eventHandler = new EventHandlerRule();
121121

122+
public ProjectConfigManager projectConfigManagerReturningNull = new ProjectConfigManager() {
123+
@Override
124+
public ProjectConfig getConfig() {
125+
return null;
126+
}
127+
128+
@Override
129+
public ProjectConfig getCachedConfig() {
130+
return null;
131+
}
132+
133+
@Override
134+
public String getSDKKey() {
135+
return null;
136+
}
137+
};
138+
122139
@Rule
123140
@SuppressFBWarnings("URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
124141
public RuleChain ruleChain = RuleChain.outerRule(thrown)
@@ -4505,13 +4522,13 @@ public void isValidReturnsTrueWhenClientIsValid() throws Exception {
45054522

45064523
@Test
45074524
public void testGetNotificationCenter() {
4508-
Optimizely optimizely = optimizelyBuilder.withConfigManager(() -> null).build();
4525+
Optimizely optimizely = optimizelyBuilder.withConfigManager(projectConfigManagerReturningNull).build();
45094526
assertEquals(optimizely.notificationCenter, optimizely.getNotificationCenter());
45104527
}
45114528

45124529
@Test
45134530
public void testAddTrackNotificationHandler() {
4514-
Optimizely optimizely = optimizelyBuilder.withConfigManager(() -> null).build();
4531+
Optimizely optimizely = optimizelyBuilder.withConfigManager(projectConfigManagerReturningNull).build();
45154532
NotificationManager<TrackNotification> manager = optimizely.getNotificationCenter()
45164533
.getNotificationManager(TrackNotification.class);
45174534

@@ -4521,7 +4538,7 @@ public void testAddTrackNotificationHandler() {
45214538

45224539
@Test
45234540
public void testAddDecisionNotificationHandler() {
4524-
Optimizely optimizely = optimizelyBuilder.withConfigManager(() -> null).build();
4541+
Optimizely optimizely = optimizelyBuilder.withConfigManager(projectConfigManagerReturningNull).build();
45254542
NotificationManager<DecisionNotification> manager = optimizely.getNotificationCenter()
45264543
.getNotificationManager(DecisionNotification.class);
45274544

@@ -4531,7 +4548,7 @@ public void testAddDecisionNotificationHandler() {
45314548

45324549
@Test
45334550
public void testAddUpdateConfigNotificationHandler() {
4534-
Optimizely optimizely = optimizelyBuilder.withConfigManager(() -> null).build();
4551+
Optimizely optimizely = optimizelyBuilder.withConfigManager(projectConfigManagerReturningNull).build();
45354552
NotificationManager<UpdateConfigNotification> manager = optimizely.getNotificationCenter()
45364553
.getNotificationManager(UpdateConfigNotification.class);
45374554

@@ -4541,7 +4558,7 @@ public void testAddUpdateConfigNotificationHandler() {
45414558

45424559
@Test
45434560
public void testAddLogEventNotificationHandler() {
4544-
Optimizely optimizely = optimizelyBuilder.withConfigManager(() -> null).build();
4561+
Optimizely optimizely = optimizelyBuilder.withConfigManager(projectConfigManagerReturningNull).build();
45454562
NotificationManager<LogEvent> manager = optimizely.getNotificationCenter()
45464563
.getNotificationManager(LogEvent.class);
45474564

@@ -4713,24 +4730,6 @@ public void initODPManagerWithProjectConfig() throws IOException {
47134730
verify(mockODPManager, times(1)).updateSettings(any(), any(), any());
47144731
}
47154732

4716-
@Test
4717-
public void updateODPManagerWhenConfigUpdates() throws IOException {
4718-
ODPEventManager mockODPEventManager = mock(ODPEventManager.class);
4719-
ODPManager mockODPManager = mock(ODPManager.class);
4720-
NotificationCenter mockNotificationCenter = mock(NotificationCenter.class);
4721-
4722-
Mockito.when(mockODPManager.getEventManager()).thenReturn(mockODPEventManager);
4723-
Optimizely.builder()
4724-
.withDatafile(validConfigJsonV4())
4725-
.withNotificationCenter(mockNotificationCenter)
4726-
.withODPManager(mockODPManager)
4727-
.build();
4728-
4729-
verify(mockODPManager, times(1)).updateSettings(any(), any(), any());
4730-
4731-
Mockito.verify(mockNotificationCenter, times(1)).addNotificationHandler(any(), any());
4732-
}
4733-
47344733
@Test
47354734
public void sendODPEvent() {
47364735
ProjectConfigManager mockProjectConfigManager = mock(ProjectConfigManager.class);

0 commit comments

Comments
 (0)