Skip to content

Commit 87a726a

Browse files
authored
Feature/1.0.2 featbit java sdk v1.0.2 (#5)
1 parent 18e0ffd commit 87a726a

25 files changed

+2011
-280
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,6 @@ fabric.properties
116116
# test data
117117
/src/test/resources/simplelogger.properties
118118
/src/test/java/co/featbit/Demos.java
119+
120+
#sonarlint
121+
/.idea/sonarlint/issuestore/index.pb

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ install the sdk in using maven
4545
<dependency>
4646
<groupId>co.featbit</groupId>
4747
<artifactId>Featbit-Java-SDK</artifactId>
48-
<version>1.0.1</version>
48+
<version>1.0.2</version>
4949
</dependency>
5050
</dependencies>
5151
```

pom.xml

+12-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>co.featbit</groupId>
88
<artifactId>featbit-java-sdk</artifactId>
9-
<version>1.0.1</version>
9+
<version>1.0.2</version>
1010

1111
<name>featbit/featbit-java-sdk</name>
1212

@@ -52,6 +52,7 @@
5252
<slf4j-version>1.7.35</slf4j-version>
5353
<ttl-version>2.12.6</ttl-version>
5454
<junit-version>5.8.1</junit-version>
55+
<mock-version>4.3</mock-version>
5556
</properties>
5657

5758
<dependencies>
@@ -111,6 +112,15 @@
111112
<scope>test</scope>
112113
</dependency>
113114

115+
<dependency>
116+
<groupId>org.easymock</groupId>
117+
<artifactId>easymock</artifactId>
118+
<version>${mock-version}</version>
119+
<scope>test</scope>
120+
</dependency>
121+
122+
123+
114124
</dependencies>
115125

116126
<distributionManagement>
@@ -207,4 +217,4 @@
207217
</plugins>
208218
</build>
209219

210-
</project>
220+
</project>

src/main/java/co/featbit/server/DataModel.java

+21-5
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22

33
import co.featbit.commons.json.JsonHelper;
44
import co.featbit.server.exterior.DataStoreTypes;
5+
import com.google.common.annotations.VisibleForTesting;
56
import com.google.common.collect.ImmutableMap;
67
import com.google.gson.annotations.Expose;
78
import com.google.gson.annotations.JsonAdapter;
9+
import org.jetbrains.annotations.NotNull;
810

9-
import java.time.Instant;
1011
import java.util.Collections;
1112
import java.util.Date;
1213
import java.util.List;
@@ -47,7 +48,12 @@ public Long getTimestamp() {
4748

4849
@Override
4950
public Integer getType() {
50-
return FFC_ARCHIVED_ITEM;
51+
return FB_ARCHIVED_ITEM;
52+
}
53+
54+
@Override
55+
public int compareTo(@NotNull DataStoreTypes.Item o) {
56+
return timestamp.compareTo(o.getTimestamp());
5157
}
5258
}
5359

@@ -105,8 +111,8 @@ boolean isProcessData() {
105111
*/
106112
@JsonAdapter(JsonHelper.AfterJsonParseDeserializableTypeAdapterFactory.class)
107113
static class Data implements JsonHelper.AfterJsonParseDeserializable {
108-
109-
private final String eventType;
114+
@VisibleForTesting
115+
/*private*/ String eventType;
110116
private final List<FeatureFlag> featureFlags;
111117
private final List<Segment> segments;
112118
private Long timestamp;
@@ -228,6 +234,11 @@ public DataStoreTypes.Item toArchivedItem() {
228234
public void afterDeserialization() {
229235
this.timestamp = updatedAt.getTime();
230236
}
237+
238+
@Override
239+
public int compareTo(@NotNull DataStoreTypes.Item o) {
240+
return timestamp.compareTo(o.getTimestamp());
241+
}
231242
}
232243

233244
@JsonAdapter(JsonHelper.AfterJsonParseDeserializableTypeAdapterFactory.class)
@@ -288,7 +299,7 @@ public Long getTimestamp() {
288299

289300
@Override
290301
public Integer getType() {
291-
return FFC_FEATURE_FLAG;
302+
return FB_FEATURE_FLAG;
292303
}
293304

294305
public boolean exptIncludeAllTargets() {
@@ -346,6 +357,11 @@ public void afterDeserialization() {
346357
this.variationMap = builder.build();
347358
}
348359
}
360+
361+
@Override
362+
public int compareTo(@NotNull DataStoreTypes.Item o) {
363+
return timestamp.compareTo(o.getTimestamp());
364+
}
349365
}
350366

351367
static final class Variation {

src/main/java/co/featbit/server/EvaluatorImp.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
final class EvaluatorImp extends Evaluator {
1818

19-
public EvaluatorImp(Getter<DataModel.FeatureFlag> flagGetter, Getter<DataModel.Segment> segmentGetter) {
19+
EvaluatorImp(Getter<DataModel.FeatureFlag> flagGetter, Getter<DataModel.Segment> segmentGetter) {
2020
super(flagGetter, segmentGetter);
2121
}
2222

src/main/java/co/featbit/server/FBClientImp.java

+15-7
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import co.featbit.server.exterior.FBClient;
1313
import co.featbit.server.exterior.InsightProcessor;
1414
import com.google.common.collect.ImmutableMap;
15-
import org.apache.commons.codec.binary.Base64;
1615
import org.apache.commons.lang3.BooleanUtils;
1716
import org.apache.commons.lang3.StringUtils;
1817
import org.slf4j.Logger;
@@ -105,9 +104,8 @@ public FBClientImp(String envSecret, FBConfig config) {
105104
checkNotNull(config, "FBConfig Should not be null");
106105
this.offline = config.isOffline();
107106
if (!this.offline) {
108-
checkArgument(Base64.isBase64(envSecret), "envSecret is invalid");
109-
checkArgument(Utils.isUrl(config.getStreamingURL()), "streaming uri is invalid");
110-
checkArgument(Utils.isUrl(config.getEventURL()), "event uri is invalid");
107+
checkArgument(Utils.isValidEnvSecret(envSecret), "envSecret is invalid");
108+
checkArgument(Utils.isUrl(config.getStreamingURL()) || Utils.isUrl(config.getEventURL()), "streaming or event url is invalid");
111109
}
112110
ContextImp context = new ContextImp(envSecret, config);
113111
//init components
@@ -155,7 +153,7 @@ public FBClientImp(String envSecret, FBConfig config) {
155153
logger.error("FFC JAVA SDK: exception encountered when waiting for data update", e);
156154
}
157155

158-
if (!this.storage.isInitialized() && !offline) {
156+
if (!this.dataSynchronizer.isInitialized() && !offline) {
159157
logger.info("FFC JAVA SDK: SDK was not successfully initialized");
160158
}
161159
}
@@ -313,7 +311,7 @@ public boolean isFlagKnown(String featureKey) {
313311
logger.warn("FFC JAVA SDK: isFlagKnown is called before Java SDK client is initialized for feature flag");
314312
return false;
315313
}
316-
return getFlagInternal(featureKey) == null;
314+
return getFlagInternal(featureKey) != null;
317315
} catch (Exception ex) {
318316
logger.error("FFC JAVA SDK: unexpected error in isFlagKnown", ex);
319317
}
@@ -348,7 +346,7 @@ public boolean initializeFromExternalJson(String json) {
348346
Map<DataStoreTypes.Category, Map<String, DataStoreTypes.Item>> allDataInStorageType = allData.toStorageType();
349347
boolean res = dataUpdater.init(allDataInStorageType, version);
350348
if (res) {
351-
dataUpdater.updateStatus(Status.StateType.OK, null);
349+
dataUpdater.updateStatus(Status.State.OKState());
352350
}
353351
return res;
354352
}
@@ -400,6 +398,16 @@ public void flush() {
400398
this.insightProcessor.flush();
401399
}
402400

401+
@Override
402+
public void identify(FBUser user) {
403+
if (user == null) {
404+
Loggers.CLIENT.warn("FFC JAVA SDK: user invalid");
405+
return;
406+
}
407+
InsightTypes.Event event = InsightTypes.UserEvent.of(user);
408+
insightProcessor.send(event);
409+
}
410+
403411
@Override
404412
public void trackMetric(FBUser user, String eventName) {
405413
trackMetric(user, eventName, 1.0);

src/main/java/co/featbit/server/InsightTypes.java

+42-10
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,27 @@ public Event add(Object element) {
5151
}
5252
}
5353

54+
@JsonAdapter(UserEventSerializer.class)
55+
final static class UserEvent extends Event {
56+
private UserEvent(FBUser user) {
57+
super(user);
58+
}
59+
60+
static UserEvent of(FBUser user) {
61+
return new UserEvent(user);
62+
}
63+
64+
@Override
65+
public boolean isSendEvent() {
66+
return user != null;
67+
}
68+
69+
@Override
70+
public Event add(Object element) {
71+
return this;
72+
}
73+
}
74+
5475
@JsonAdapter(FlagEventSerializer.class)
5576
final static class FlagEvent extends Event {
5677
private final List<FlagEventVariation> userVariations = new ArrayList<>();
@@ -170,6 +191,13 @@ public String getAppType() {
170191
}
171192
}
172193

194+
final static class UserEventSerializer implements JsonSerializer<UserEvent> {
195+
@Override
196+
public JsonElement serialize(UserEvent userEvent, Type type, JsonSerializationContext jsonSerializationContext) {
197+
return serializeUser(userEvent.getUser());
198+
}
199+
}
200+
173201
final static class FlagEventSerializer implements JsonSerializer<FlagEvent> {
174202

175203
@Override
@@ -181,7 +209,7 @@ public JsonElement serialize(FlagEvent flagEvent, Type type, JsonSerializationCo
181209
JsonObject var = new JsonObject();
182210
var.addProperty("featureFlagKey", variation.getFeatureFlagKeyName());
183211
var.addProperty("sendToExperiment", variation.getVariation().isSendToExperiment());
184-
var.addProperty("timestamp", Instant.now().toEpochMilli());
212+
var.addProperty("timestamp", variation.getTimestamp());
185213
JsonObject v = new JsonObject();
186214
v.addProperty("id", variation.getVariation().getIndex());
187215
v.addProperty("value", variation.getVariation().getValue());
@@ -232,26 +260,28 @@ private static JsonObject serializeUser(FBUser user) {
232260
}
233261

234262
enum InsightMessageType {
235-
FLAGS, FLUSH, SHUTDOWN, METRICS,
263+
FLAGS, FLUSH, SHUTDOWN, METRICS, USERS, STATISTICS
236264
}
237265

238266
static final class InsightMessage {
239267
private final InsightMessageType type;
240268
private final Event event;
241-
private final Semaphore waitLock;
269+
private final Object waitLock;
242270

243271
// waitLock is initialized only when you need to wait until the message is completely handled
244272
// Ex, shutdown, in this case, we should to wait until all events are sent to server
245-
InsightMessage(InsightMessageType type, Event event, boolean awaitTermination) {
273+
InsightMessage(InsightMessageType type, Event event, boolean awaitToComplete) {
246274
this.type = type;
247275
this.event = event;
248276
// permit = 0, so wait until a permit releases
249-
this.waitLock = awaitTermination ? new Semaphore(0) : null;
277+
this.waitLock = awaitToComplete ? new Object() : null;
250278
}
251279

252280
public void completed() {
253281
if (waitLock != null) {
254-
waitLock.release();
282+
synchronized (waitLock) {
283+
waitLock.notifyAll();
284+
}
255285
}
256286
}
257287

@@ -260,10 +290,12 @@ public void waitForComplete() {
260290
return;
261291
}
262292
while (true) {
263-
try {
264-
waitLock.acquire();
265-
return;
266-
} catch (InterruptedException ignore) {
293+
synchronized (waitLock) {
294+
try {
295+
waitLock.wait();
296+
return;
297+
} catch (InterruptedException ignore) {
298+
}
267299
}
268300
}
269301

src/main/java/co/featbit/server/Insights.java

+7-4
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ public void send(InsightTypes.Event event) {
4949
putEventAsync(InsightTypes.InsightMessageType.FLAGS, event);
5050
} else if (event instanceof InsightTypes.MetricEvent) {
5151
putEventAsync(InsightTypes.InsightMessageType.METRICS, event);
52+
} else if (event instanceof InsightTypes.UserEvent) {
53+
putEventAsync(InsightTypes.InsightMessageType.USERS, event);
5254
} else {
5355
Loggers.EVENTS.debug("ignore event type: {}", event.getClass().getName());
5456
}
@@ -133,7 +135,7 @@ public Boolean run() {
133135
Loggers.EVENTS.debug("paload size: {}", partition.size());
134136
});
135137
} catch (Exception unexpected) {
136-
Loggers.EVENTS.error("FFC JAVA SDK: unexpected error in sending payload: {}", unexpected.getMessage());
138+
Loggers.EVENTS.error("FFC JAVA SDK: unexpected error in sending payload", unexpected);
137139
return false;
138140
}
139141
return true;
@@ -187,6 +189,7 @@ private void dispatchEvents() {
187189
switch (message.getType()) {
188190
case FLAGS:
189191
case METRICS:
192+
case USERS:
190193
putEventToNextBuffer(message.getEvent());
191194
break;
192195
case FLUSH:
@@ -199,12 +202,12 @@ private void dispatchEvents() {
199202
}
200203
message.completed();
201204
} catch (Exception unexpected) {
202-
Loggers.EVENTS.error("FFC JAVA SDK: unexpected error in event dispatcher {}", unexpected.getMessage());
205+
Loggers.EVENTS.error("FFC JAVA SDK: unexpected error in event dispatcher", unexpected);
203206
}
204207
}
205208
} catch (InterruptedException ignore) {
206209
} catch (Exception unexpected) {
207-
Loggers.EVENTS.error("FFC JAVA SDK: unexpected error in event dispatcher {}", unexpected.getMessage());
210+
Loggers.EVENTS.error("FFC JAVA SDK: unexpected error in event dispatcher", unexpected);
208211
}
209212
}
210213
}
@@ -266,7 +269,7 @@ private void shutdown() {
266269
config.getRight().close();
267270
}
268271
} catch (Exception unexpected) {
269-
Loggers.EVENTS.error("FFC JAVA SDK: unexpected error when closing event dispatcher: {}", unexpected.getMessage());
272+
Loggers.EVENTS.error("FFC JAVA SDK: unexpected error when closing event dispatcher", unexpected);
270273
}
271274
}
272275

0 commit comments

Comments
 (0)