Skip to content

Commit 54b7b3d

Browse files
authored
Merge pull request #45 from optimizely/elliot/notification3
Notification listeners
2 parents 9167e52 + 18ed45b commit 54b7b3d

File tree

7 files changed

+468
-1
lines changed

7 files changed

+468
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
- Change `UserExperimentRecord` to `UserProfile`
44
- Add support for IP anonymization
5+
- Add `NotificationListener` for SDK events
56

67
## 1.1.0
78

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
import com.optimizely.ab.event.internal.EventBuilderV2;
4040
import com.optimizely.ab.event.internal.payload.Event.ClientEngine;
4141
import com.optimizely.ab.internal.ProjectValidationUtils;
42+
import com.optimizely.ab.notification.NotificationListener;
43+
import com.optimizely.ab.notification.NotificationBroadcaster;
4244

4345
import org.slf4j.Logger;
4446
import org.slf4j.LoggerFactory;
@@ -89,6 +91,7 @@ public class Optimizely {
8991
@VisibleForTesting final ProjectConfig projectConfig;
9092
@VisibleForTesting final EventHandler eventHandler;
9193
@VisibleForTesting final ErrorHandler errorHandler;
94+
@VisibleForTesting final NotificationBroadcaster notificationBroadcaster = new NotificationBroadcaster();
9295

9396
private Optimizely(@Nonnull ProjectConfig projectConfig,
9497
@Nonnull Bucketer bucketer,
@@ -180,6 +183,8 @@ private Optimizely(@Nonnull ProjectConfig projectConfig,
180183
logger.error("Unexpected exception in event dispatcher", e);
181184
}
182185

186+
notificationBroadcaster.broadcastExperimentActivated(experiment, userId, attributes, variation);
187+
183188
return variation;
184189
}
185190

@@ -438,6 +443,33 @@ private static ProjectConfig getProjectConfig(String datafile) throws ConfigPars
438443
return DefaultConfigParser.getInstance().parseProjectConfig(datafile);
439444
}
440445

446+
//======== Notification listeners ========//
447+
448+
/**
449+
* Add a {@link NotificationListener} if it does not exist already.
450+
*
451+
* @param listener listener to add
452+
*/
453+
public void addNotificationListener(@Nonnull NotificationListener listener) {
454+
notificationBroadcaster.addListener(listener);
455+
}
456+
457+
/**
458+
* Remove a {@link NotificationListener} if it exists.
459+
*
460+
* @param listener listener to remove
461+
*/
462+
public void removeNotificationListener(@Nonnull NotificationListener listener) {
463+
notificationBroadcaster.removeListener(listener);
464+
}
465+
466+
/**
467+
* Remove all {@link NotificationListener}.
468+
*/
469+
public void clearNotificationListeners() {
470+
notificationBroadcaster.clearListeners();
471+
}
472+
441473
//======== Helper methods ========//
442474

443475
/**

core-api/src/main/java/com/optimizely/ab/event/internal/EventBuilderV2.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,4 +209,4 @@ private List<LayerState> createLayerStates(ProjectConfig projectConfig, Bucketer
209209

210210
return layerStates;
211211
}
212-
}
212+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/**
2+
*
3+
* Copyright 2016, 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.notification;
18+
19+
import com.optimizely.ab.annotations.VisibleForTesting;
20+
import com.optimizely.ab.config.Experiment;
21+
import com.optimizely.ab.config.Variation;
22+
23+
import org.slf4j.Logger;
24+
import org.slf4j.LoggerFactory;
25+
26+
import java.util.HashSet;
27+
import java.util.Map;
28+
29+
import javax.annotation.Nonnull;
30+
31+
/**
32+
* Manages Optimizely SDK notification listeners and broadcasts messages.
33+
*/
34+
public class NotificationBroadcaster {
35+
36+
private static final Logger logger = LoggerFactory.getLogger(NotificationBroadcaster.class);
37+
38+
@VisibleForTesting final HashSet<NotificationListener> listeners =
39+
new HashSet<NotificationListener>();
40+
41+
/**
42+
* Add a listener if it does not exist already.
43+
*
44+
* @param listener listener to add
45+
*/
46+
public void addListener(@Nonnull NotificationListener listener) {
47+
if (listeners.contains(listener)) {
48+
logger.debug("Notification listener was not added because it already existed");
49+
return;
50+
}
51+
52+
listeners.add(listener);
53+
logger.debug("Notification listener was added");
54+
}
55+
56+
/**
57+
* Remove a listener if it exists.
58+
*
59+
* @param listener listener to remove
60+
*/
61+
public void removeListener(@Nonnull NotificationListener listener) {
62+
if (listeners.contains(listener)) {
63+
listeners.remove(listener);
64+
logger.debug("Notification listener was removed");
65+
return;
66+
}
67+
68+
logger.debug("Notification listener was not removed because it did not exist");
69+
}
70+
71+
/**
72+
* Remove all listeners.
73+
*/
74+
public void clearListeners() {
75+
listeners.clear();
76+
logger.debug("Notification listeners were cleared");
77+
}
78+
79+
/**
80+
* Notify listeners that an Optimizely experiment has been activated.
81+
*
82+
* @param experiment the key of the activated experiment
83+
* @param userId the id of the user
84+
* @param attributes a map of attributes about the user
85+
* @param variation the key of the variation that was bucketed
86+
*/
87+
public void broadcastExperimentActivated(@Nonnull Experiment experiment,
88+
@Nonnull String userId,
89+
@Nonnull Map<String, String> attributes,
90+
@Nonnull Variation variation) {
91+
for (final NotificationListener iterListener : listeners) {
92+
iterListener.onExperimentActivated(experiment, userId, attributes, variation);
93+
}
94+
}
95+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/**
2+
*
3+
* Copyright 2016, 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.notification;
18+
19+
import com.optimizely.ab.config.Experiment;
20+
import com.optimizely.ab.config.Variation;
21+
22+
import java.util.Map;
23+
24+
import javax.annotation.Nonnull;
25+
26+
/**
27+
* Abstract class for Optimizely notification listeners.
28+
* <p>
29+
* We use an abstract class here instead of an interface for convenience of use and backwards
30+
* compatibility in the future. An interface would force consumers to implement every method defined
31+
* on it as well as update their application code with new method implementations every time new
32+
* methods are added to the interface in the SDK. An abstract classes allows consumers to override
33+
* just the methods they need.
34+
*/
35+
public abstract class NotificationListener {
36+
37+
/**
38+
* Listener that is called after an experiment has been activated.
39+
*
40+
* @param experiment the activated experiment
41+
* @param userId the id of the user
42+
* @param attributes a map of attributes about the user
43+
* @param variation the key of the variation that was bucketed
44+
*/
45+
public void onExperimentActivated(@Nonnull Experiment experiment,
46+
@Nonnull String userId,
47+
@Nonnull Map<String, String> attributes,
48+
@Nonnull Variation variation) {
49+
}
50+
}

0 commit comments

Comments
 (0)