2929import com .optimizely .ab .config .ProjectConfig ;
3030import com .optimizely .ab .config .Variation ;
3131import com .optimizely .ab .config .parser .ConfigParseException ;
32- import com .optimizely .ab .config .parser .DefaultConfigParser ;
3332import com .optimizely .ab .error .ErrorHandler ;
3433import com .optimizely .ab .error .NoOpErrorHandler ;
3534import com .optimizely .ab .event .EventHandler ;
@@ -80,33 +79,65 @@ public class Optimizely {
8079
8180 private static final Logger logger = LoggerFactory .getLogger (Optimizely .class );
8281
83- @ VisibleForTesting final DecisionService decisionService ;
82+ @ VisibleForTesting DecisionService decisionService ;
8483 @ VisibleForTesting final EventFactory eventFactory ;
85- @ VisibleForTesting final ProjectConfig projectConfig ;
84+ @ VisibleForTesting ProjectConfig projectConfig ;
8685 @ VisibleForTesting final EventHandler eventHandler ;
8786 @ VisibleForTesting final ErrorHandler errorHandler ;
87+ private boolean isValid ;
8888 public final NotificationCenter notificationCenter = new NotificationCenter ();
8989
9090 @ Nullable private final UserProfileService userProfileService ;
9191
92- private Optimizely (@ Nonnull ProjectConfig projectConfig ,
93- @ Nonnull DecisionService decisionService ,
94- @ Nonnull EventHandler eventHandler ,
92+ private Optimizely (@ Nonnull EventHandler eventHandler ,
9593 @ Nonnull EventFactory eventFactory ,
9694 @ Nonnull ErrorHandler errorHandler ,
95+ @ Nullable DecisionService decisionService ,
9796 @ Nullable UserProfileService userProfileService ) {
98- this .projectConfig = projectConfig ;
9997 this .decisionService = decisionService ;
10098 this .eventHandler = eventHandler ;
10199 this .eventFactory = eventFactory ;
102100 this .errorHandler = errorHandler ;
103101 this .userProfileService = userProfileService ;
104102 }
105103
106- // Do work here that should be done once per Optimizely lifecycle
104+ /**
105+ * Initializes the SDK state. Can conceivably re-use this in the future with datafile sync where
106+ * we can re-initialize the SDK instead of re-instantiating.
107+ */
107108 @ VisibleForTesting
108- void initialize () {
109+ void initialize (@ Nonnull String datafile , @ Nullable ProjectConfig projectConfig ) {
110+ if (projectConfig == null ) {
111+ try {
112+ projectConfig = new ProjectConfig .Builder ()
113+ .withDatafile (datafile )
114+ .build ();
115+ isValid = true ;
116+ logger .info ("Datafile is valid" );
117+ } catch (ConfigParseException ex ) {
118+ logger .error ("Unable to parse the datafile" , ex );
119+ logger .info ("Datafile is invalid" );
120+ errorHandler .handleError (new OptimizelyRuntimeException (ex ));
121+ }
122+ } else {
123+ isValid = true ;
124+ }
125+
126+ this .projectConfig = projectConfig ;
127+ if (decisionService == null ) {
128+ Bucketer bucketer = new Bucketer (projectConfig );
129+ decisionService = new DecisionService (bucketer , errorHandler , projectConfig , userProfileService );
130+ }
131+ }
109132
133+ /**
134+ * Determine if the instance of the Optimizely client is valid. An instance can be deemed invalid if it was not
135+ * initialized properly due to an invalid datafile being passed in.
136+ * @return True if the Optimizely instance is valid.
137+ * False if the Optimizely instance is not valid.
138+ */
139+ public boolean isValid () {
140+ return isValid ;
110141 }
111142
112143 //======== activate calls ========//
@@ -121,6 +152,10 @@ Variation activate(@Nonnull String experimentKey,
121152 Variation activate (@ Nonnull String experimentKey ,
122153 @ Nonnull String userId ,
123154 @ Nonnull Map <String , ?> attributes ) throws UnknownExperimentException {
155+ if (!isValid ) {
156+ logger .error ("Optimizely instance is not valid, failing activate call." );
157+ return null ;
158+ }
124159
125160 if (experimentKey == null ) {
126161 logger .error ("The experimentKey parameter must be nonnull." );
@@ -165,6 +200,10 @@ Variation activate(@Nonnull ProjectConfig projectConfig,
165200 @ Nonnull Experiment experiment ,
166201 @ Nonnull String userId ,
167202 @ Nonnull Map <String , ?> attributes ) {
203+ if (!isValid ) {
204+ logger .error ("Optimizely instance is not valid, failing activate call." );
205+ return null ;
206+ }
168207
169208 if (!validateUserId (userId )){
170209 logger .info ("Not activating user \" {}\" for experiment \" {}\" ." , userId , experiment .getKey ());
@@ -236,6 +275,10 @@ public void track(@Nonnull String eventName,
236275 @ Nonnull String userId ,
237276 @ Nonnull Map <String , ?> attributes ,
238277 @ Nonnull Map <String , ?> eventTags ) throws UnknownEventTypeException {
278+ if (!isValid ) {
279+ logger .error ("Optimizely instance is not valid, failing track call." );
280+ return ;
281+ }
239282
240283 if (!validateUserId (userId )) {
241284 logger .info ("Not tracking event \" {}\" ." , eventName );
@@ -345,6 +388,11 @@ public void track(@Nonnull String eventName,
345388 public @ Nonnull Boolean isFeatureEnabled (@ Nonnull String featureKey ,
346389 @ Nonnull String userId ,
347390 @ Nonnull Map <String , ?> attributes ) {
391+ if (!isValid ) {
392+ logger .error ("Optimizely instance is not valid, failing isFeatureEnabled call." );
393+ return false ;
394+ }
395+
348396 if (featureKey == null ) {
349397 logger .warn ("The featureKey parameter must be nonnull." );
350398 return false ;
@@ -411,6 +459,11 @@ else if (userId == null) {
411459 @ Nonnull String variableKey ,
412460 @ Nonnull String userId ,
413461 @ Nonnull Map <String , ?> attributes ) {
462+ if (!isValid ) {
463+ logger .error ("Optimizely instance is not valid, failing getFeatureVariableBoolean call." );
464+ return null ;
465+ }
466+
414467 String variableValue = getFeatureVariableValueForType (
415468 featureKey ,
416469 variableKey ,
@@ -451,6 +504,11 @@ else if (userId == null) {
451504 @ Nonnull String variableKey ,
452505 @ Nonnull String userId ,
453506 @ Nonnull Map <String , ?> attributes ) {
507+ if (!isValid ) {
508+ logger .error ("Optimizely instance is not valid, failing getFeatureVariableDouble call." );
509+ return null ;
510+ }
511+
454512 String variableValue = getFeatureVariableValueForType (
455513 featureKey ,
456514 variableKey ,
@@ -496,6 +554,11 @@ else if (userId == null) {
496554 @ Nonnull String variableKey ,
497555 @ Nonnull String userId ,
498556 @ Nonnull Map <String , ?> attributes ) {
557+ if (!isValid ) {
558+ logger .error ("Optimizely instance is not valid, failing getFeatureVariableInteger call." );
559+ return null ;
560+ }
561+
499562 String variableValue = getFeatureVariableValueForType (
500563 featureKey ,
501564 variableKey ,
@@ -541,6 +604,11 @@ else if (userId == null) {
541604 @ Nonnull String variableKey ,
542605 @ Nonnull String userId ,
543606 @ Nonnull Map <String , ?> attributes ) {
607+ if (!isValid ) {
608+ logger .error ("Optimizely instance is not valid, failing getFeatureVariableString call." );
609+ return null ;
610+ }
611+
544612 return getFeatureVariableValueForType (
545613 featureKey ,
546614 variableKey ,
@@ -617,6 +685,11 @@ else if (userId == null) {
617685 public List <String > getEnabledFeatures (@ Nonnull String userId , @ Nonnull Map <String , ?> attributes ) {
618686 List <String > enabledFeaturesList = new ArrayList <String >();
619687
688+ if (!isValid ) {
689+ logger .error ("Optimizely instance is not valid, failing getEnabledFeatures call." );
690+ return enabledFeaturesList ;
691+ }
692+
620693 if (!validateUserId (userId )){
621694 return enabledFeaturesList ;
622695 }
@@ -660,6 +733,11 @@ Variation getVariation(@Nonnull String experimentKey,
660733 Variation getVariation (@ Nonnull String experimentKey ,
661734 @ Nonnull String userId ,
662735 @ Nonnull Map <String , ?> attributes ) {
736+ if (!isValid ) {
737+ logger .error ("Optimizely instance is not valid, failing getVariation call." );
738+ return null ;
739+ }
740+
663741 if (!validateUserId (userId )) {
664742 return null ;
665743 }
@@ -697,7 +775,10 @@ Variation getVariation(@Nonnull String experimentKey,
697775 public boolean setForcedVariation (@ Nonnull String experimentKey ,
698776 @ Nonnull String userId ,
699777 @ Nullable String variationKey ) {
700-
778+ if (!isValid ) {
779+ logger .error ("Optimizely instance is not valid, failing setForcedVariation call." );
780+ return false ;
781+ }
701782
702783 return projectConfig .setForcedVariation (experimentKey , userId , variationKey );
703784 }
@@ -715,6 +796,11 @@ public boolean setForcedVariation(@Nonnull String experimentKey,
715796 */
716797 public @ Nullable Variation getForcedVariation (@ Nonnull String experimentKey ,
717798 @ Nonnull String userId ) {
799+ if (!isValid ) {
800+ logger .error ("Optimizely instance is not valid, failing getForcedVariation call." );
801+ return null ;
802+ }
803+
718804 return projectConfig .getForcedVariation (experimentKey , userId );
719805 }
720806
@@ -869,17 +955,7 @@ protected Builder withConfig(ProjectConfig projectConfig) {
869955 return this ;
870956 }
871957
872- public Optimizely build () throws ConfigParseException {
873- if (projectConfig == null ) {
874- projectConfig = new ProjectConfig .Builder ()
875- .withDatafile (datafile )
876- .build ();
877- }
878-
879- if (bucketer == null ) {
880- bucketer = new Bucketer (projectConfig );
881- }
882-
958+ public Optimizely build () {
883959 if (clientEngine == null ) {
884960 clientEngine = ClientEngine .JAVA_SDK ;
885961 }
@@ -897,12 +973,13 @@ public Optimizely build() throws ConfigParseException {
897973 errorHandler = new NoOpErrorHandler ();
898974 }
899975
900- if (decisionService == null ) {
976+ // Used for convenience while unit testing to override/mock bucketing. This interface is NOT public and should be refactored out.
977+ if (bucketer != null && decisionService == null ) {
901978 decisionService = new DecisionService (bucketer , errorHandler , projectConfig , userProfileService );
902979 }
903980
904- Optimizely optimizely = new Optimizely (projectConfig , decisionService , eventHandler , eventFactory , errorHandler , userProfileService );
905- optimizely .initialize ();
981+ Optimizely optimizely = new Optimizely (eventHandler , eventFactory , errorHandler , decisionService , userProfileService );
982+ optimizely .initialize (datafile , projectConfig );
906983 return optimizely ;
907984 }
908985 }
0 commit comments