From e3a33410adcc72ebe7bb8cde952fbd09319e02a5 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray <57103426+arifBurakDemiray@users.noreply.github.com> Date: Wed, 27 Sep 2023 11:44:58 +0300 Subject: [PATCH 1/5] Update README.md (#77) --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f3dc87659..3c2f34a8a 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # Countly Java SDK -This repository contains the Countly Java SDK, which can be integrated into Java applications. The Countly Java SDK is intended to be used with [Countly Community Edition](https://github.com/Countly/countly-server) or [Countly Enterprise Edition](https://count.ly/product). +This repository contains the Countly Java SDK, which can be integrated into Java applications. The Countly Java SDK is intended to be used with [Countly Lite](https://github.com/Countly/countly-server), [Countly Flex](https://countly.com/flex), [Countly Enterprise](https://countly.com/enterprise). ## What is Countly? @@ -12,16 +12,16 @@ and [desktop](https://count.ly/desktop-analytics) applications. [Ensuring privac Track, measure, and take action - all without leaving Countly. * **Questions or feature requests?** [Join the Countly Community on Discord](https://discord.gg/countly) -* **Looking for the Countly Server?** [Countly Community Edition repository](https://github.com/Countly/countly-server) +* **Looking for the Countly Server?** [Countly Server repository](https://github.com/Countly/countly-server) * **Looking for other Countly SDKs?** [An overview of all Countly SDKs for mobile, web and desktop](https://support.count.ly/hc/en-us/articles/360037236571-Downloading-and-Installing-SDKs#officially-supported-sdks) ## Integrating Countly SDK in your projects For a detailed description on how to use this SDK [check out our documentation](https://support.count.ly/hc/en-us/articles/360037813891-Java). -For information about how to add the SDK to your project, please check [this section of the documentation](https://support.count.ly/hc/en-us/articles/360037813891-Java#adding-sdk-to-the-project). +For information about how to add the SDK to your project, please check [this section of the documentation](https://support.count.ly/hc/en-us/articles/360037813891-Java#h_01HABV0K6BZ251ANK02RZK3Z5H). -You can find minimal SDK integration information for your project in [this section of the documentation](https://support.count.ly/hc/en-us/articles/360037813891-Java#minimal-setup). +You can find minimal SDK integration information for your project in [this section of the documentation](https://support.count.ly/hc/en-us/articles/360037813891-Java#h_01HABV0K6C4H1G71VV85BDXV91). For an example integration of this SDK, you can have a look [here](https://github.com/Countly/countly-sdk-java/tree/master/app-java/src/main/java/ly/count/java/demo). From 818195b34b54700631a8f22d2786e05bef3c72c3 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray <57103426+arifBurakDemiray@users.noreply.github.com> Date: Wed, 27 Sep 2023 17:11:13 +0300 Subject: [PATCH 2/5] Update README.md (#80) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3c2f34a8a..dc6d067ef 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # Countly Java SDK -This repository contains the Countly Java SDK, which can be integrated into Java applications. The Countly Java SDK is intended to be used with [Countly Lite](https://github.com/Countly/countly-server), [Countly Flex](https://countly.com/flex), [Countly Enterprise](https://countly.com/enterprise). +This repository contains the Countly Java SDK, which can be integrated into Java applications. The Countly Java SDK is intended to be used with [Countly Lite](https://countly.com/lite), [Countly Flex](https://countly.com/flex), [Countly Enterprise](https://countly.com/enterprise). ## What is Countly? From e01ae5e2e42f5919a94aabc4f40c12f1ef7ec036 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray <57103426+arifBurakDemiray@users.noreply.github.com> Date: Wed, 27 Sep 2023 17:11:40 +0300 Subject: [PATCH 3/5] feat: sdk storage check fix (#79) --- .../count/java/demo/BackendModeExample.java | 11 ++++---- .../demo/BackendModePerformanceTests.java | 13 +++++---- .../main/java/ly/count/java/demo/Example.java | 7 +++-- .../sdk/java/internal/BackendModeTests.java | 28 ++++++++++--------- .../ly/count/sdk/java/internal/TestUtils.java | 14 ++++++++++ 5 files changed, 46 insertions(+), 27 deletions(-) diff --git a/app-java/src/main/java/ly/count/java/demo/BackendModeExample.java b/app-java/src/main/java/ly/count/java/demo/BackendModeExample.java index 91ee51d4c..0e1697827 100644 --- a/app-java/src/main/java/ly/count/java/demo/BackendModeExample.java +++ b/app-java/src/main/java/ly/count/java/demo/BackendModeExample.java @@ -1,13 +1,12 @@ package ly.count.java.demo; -import ly.count.sdk.java.Config; -import ly.count.sdk.java.Countly; - import java.io.File; import java.util.HashMap; import java.util.Map; import java.util.Scanner; import java.util.concurrent.CountDownLatch; +import ly.count.sdk.java.Config; +import ly.count.sdk.java.Countly; public class BackendModeExample { final static String DEVICE_ID = "device-id"; @@ -314,8 +313,10 @@ public static void main(String[] args) throws Exception { String[] sdkStorageRootPath = { System.getProperty("user.home"), "__COUNTLY", "java_test" }; File sdkStorageRootDirectory = new File(String.join(File.separator, sdkStorageRootPath)); - if(sdkStorageRootDirectory.mkdirs()){ - System.out.println("Directory creation failed"); + if (!(sdkStorageRootDirectory.exists() && sdkStorageRootDirectory.isDirectory())) { + if (!sdkStorageRootDirectory.mkdirs()) { + System.out.println("Directory creation failed"); + } } // Main initialization call, SDK can be used after this one is done diff --git a/app-java/src/main/java/ly/count/java/demo/BackendModePerformanceTests.java b/app-java/src/main/java/ly/count/java/demo/BackendModePerformanceTests.java index 7645fe756..1736a21b1 100644 --- a/app-java/src/main/java/ly/count/java/demo/BackendModePerformanceTests.java +++ b/app-java/src/main/java/ly/count/java/demo/BackendModePerformanceTests.java @@ -1,13 +1,12 @@ package ly.count.java.demo; -import ly.count.sdk.java.Config; -import ly.count.sdk.java.Countly; -import ly.count.sdk.java.internal.Device; - import java.io.File; import java.util.HashMap; import java.util.Map; import java.util.Scanner; +import ly.count.sdk.java.Config; +import ly.count.sdk.java.Countly; +import ly.count.sdk.java.internal.Device; public class BackendModePerformanceTests { final static String DEVICE_ID = "device-id"; @@ -27,8 +26,10 @@ private static void initSDK(int eventQueueSize, int requestQueueSize) { String[] sdkStorageRootPath = { System.getProperty("user.home"), "__COUNTLY", "java_test" }; File sdkStorageRootDirectory = new File(String.join(File.separator, sdkStorageRootPath)); - if(sdkStorageRootDirectory.mkdirs()){ - System.out.println("Directory creation failed"); + if (!(sdkStorageRootDirectory.exists() && sdkStorageRootDirectory.isDirectory())) { + if (!sdkStorageRootDirectory.mkdirs()) { + System.out.println("Directory creation failed"); + } } // Main initialization call, SDK can be used after this one is done diff --git a/app-java/src/main/java/ly/count/java/demo/Example.java b/app-java/src/main/java/ly/count/java/demo/Example.java index 4367ab115..8278e9d55 100755 --- a/app-java/src/main/java/ly/count/java/demo/Example.java +++ b/app-java/src/main/java/ly/count/java/demo/Example.java @@ -4,7 +4,6 @@ import java.util.HashMap; import java.util.Map; import java.util.Scanner; - import ly.count.sdk.java.Config; import ly.count.sdk.java.Countly; import ly.count.sdk.java.internal.LogCallback; @@ -125,8 +124,10 @@ public static void main(String[] args) throws Exception { String[] sdkStorageRootPath = { System.getProperty("user.home"), "__COUNTLY", "java_test" }; File sdkStorageRootDirectory = new File(String.join(File.separator, sdkStorageRootPath)); - if(sdkStorageRootDirectory.mkdirs()){ - System.out.println("Directory creation failed"); + if (!(sdkStorageRootDirectory.exists() && sdkStorageRootDirectory.isDirectory())) { + if (!sdkStorageRootDirectory.mkdirs()) { + System.out.println("Directory creation failed"); + } } Config config = new Config(COUNTLY_SERVER_URL, COUNTLY_APP_KEY, sdkStorageRootDirectory) diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/BackendModeTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/BackendModeTests.java index 7ff3ff78c..289771702 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/BackendModeTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/BackendModeTests.java @@ -1,19 +1,25 @@ package ly.count.sdk.java.internal; +import java.io.File; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.math.BigDecimal; +import java.util.Calendar; +import java.util.HashMap; +import java.util.Map; import ly.count.sdk.java.Config; import ly.count.sdk.java.Countly; import org.json.JSONArray; import org.json.JSONObject; -import org.junit.*; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; -import java.io.File; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.math.BigDecimal; -import java.util.*; - @RunWith(JUnit4.class) public class BackendModeTests { private ModuleBackendMode moduleBackendMode; @@ -25,12 +31,8 @@ public static void init() { cc.setEventQueueSizeToSend(4).enableBackendMode(); // System specific folder structure - String[] sdkStorageRootPath = { System.getProperty("user.home"), "__COUNTLY", "java_test" }; - File sdkStorageRootDirectory = new File(String.join(File.separator, sdkStorageRootPath)); - - if(sdkStorageRootDirectory.mkdirs()){ - System.out.println("Directory creation failed"); - } + File sdkStorageRootDirectory = TestUtils.getSdkStorageRootDirectory(); + TestUtils.checkSdkStorageRootDirectoryExist(sdkStorageRootDirectory); Countly.init(sdkStorageRootDirectory, cc); } diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java index e393c1ce3..502466a48 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java @@ -151,4 +151,18 @@ private static Map parseRequestParams(File file) throws IOExcept return paramMap; } } + + static File getSdkStorageRootDirectory() { + // System specific folder structure + String[] sdkStorageRootPath = { System.getProperty("user.home"), "__COUNTLY", "java_test" }; + return new File(String.join(File.separator, sdkStorageRootPath)); + } + + static void checkSdkStorageRootDirectoryExist(File directory) { + if (!(directory.exists() && directory.isDirectory())) { + if (!directory.mkdirs()) { + throw new RuntimeException("Directory creation failed"); + } + } + } } \ No newline at end of file From f6627fcabb4363d28d80f661f1c93449661d6d88 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray <57103426+arifBurakDemiray@users.noreply.github.com> Date: Wed, 27 Sep 2023 17:12:05 +0300 Subject: [PATCH 4/5] feat: add required time params (#81) --- .../ly/count/sdk/java/internal/ModuleRequests.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleRequests.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleRequests.java index d83af81c7..6931ba2b6 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleRequests.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleRequests.java @@ -1,7 +1,6 @@ package ly.count.sdk.java.internal; import java.util.concurrent.Future; - import ly.count.sdk.java.User; /** @@ -181,6 +180,13 @@ public static void injectParams(CtxCore ctx, ParamsInjector injector) { } } + static void addRequiredTimeParams(Request request) { + request.params.add("timestamp", Device.dev.uniqueTimestamp()) + .add("tz", Device.dev.getTimezoneOffset()) + .add("hour", Device.dev.currentHour()) + .add("dow", Device.dev.currentDayOfWeek()); + } + static Request addRequired(InternalConfig config, Request request) { if (request.isEmpty()) { @@ -246,10 +252,7 @@ public static Future pushAsync(final CtxCore ctx, final Request request return null; } - request.params.add("timestamp", Device.dev.uniqueTimestamp()) - .add("tz", Device.dev.getTimezoneOffset()) - .add("hour", Device.dev.currentHour()) - .add("dow", Device.dev.currentDayOfWeek()); + addRequiredTimeParams(request); return Storage.pushAsync(ctx, request, param -> { SDKCore.instance.onRequest(ctx, request); From f1223226e569d6300e44f7c07f2ccc148f3cb24f Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray <57103426+arifBurakDemiray@users.noreply.github.com> Date: Wed, 27 Sep 2023 17:17:16 +0300 Subject: [PATCH 5/5] [Java] Create a instant request maker (#75) * fix: usage of callbacks * feat: immediate request * feat: irGenerator to cnfig * feat: immediate req test * fix: stop countly * fix: directory creatioon * fix: folder checks * feat: add feedback to sdk core and countly * doc: comments for new ir * revert: directory * revert: sdk storage thingies * revert: changes * fix: storage * fix: revert * fix: revert * fix: remove kine * fix: rename try to test * fix: remove unnecessary block * feat: revert feedback related things * revert: feedback related * feat: irg override test --- .../main/java/ly/count/sdk/java/Config.java | 11 +- .../main/java/ly/count/sdk/java/Countly.java | 8 +- .../sdk/java/internal/DefaultNetworking.java | 5 + .../internal/ImmediateRequestGenerator.java | 17 +++ .../sdk/java/internal/ImmediateRequestI.java | 17 +++ .../java/internal/ImmediateRequestMaker.java | 116 ++++++++++++++++++ .../sdk/java/internal/InternalConfig.java | 3 +- .../count/sdk/java/internal/Networking.java | 2 + .../ly/count/sdk/java/internal/SDKCore.java | 16 ++- .../ly/count/sdk/java/internal/Transport.java | 20 +-- .../sdk/java/internal/BackendModeTests.java | 2 +- .../java/internal/ImmediateRequestTest.java | 84 +++++++++++++ .../ly/count/sdk/java/internal/TestUtils.java | 38 +++++- 13 files changed, 313 insertions(+), 26 deletions(-) create mode 100644 sdk-java/src/main/java/ly/count/sdk/java/internal/ImmediateRequestGenerator.java create mode 100644 sdk-java/src/main/java/ly/count/sdk/java/internal/ImmediateRequestI.java create mode 100644 sdk-java/src/main/java/ly/count/sdk/java/internal/ImmediateRequestMaker.java create mode 100644 sdk-java/src/test/java/ly/count/sdk/java/internal/ImmediateRequestTest.java diff --git a/sdk-java/src/main/java/ly/count/sdk/java/Config.java b/sdk-java/src/main/java/ly/count/sdk/java/Config.java index 932766fe4..9fceca8b5 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/Config.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/Config.java @@ -12,8 +12,13 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; - -import ly.count.sdk.java.internal.*; +import ly.count.sdk.java.internal.Byteable; +import ly.count.sdk.java.internal.CoreFeature; +import ly.count.sdk.java.internal.ImmediateRequestGenerator; +import ly.count.sdk.java.internal.Log; +import ly.count.sdk.java.internal.LogCallback; +import ly.count.sdk.java.internal.ModuleBase; +import ly.count.sdk.java.internal.Utils; /** * Countly configuration object. @@ -462,6 +467,8 @@ public boolean restore(byte[] data, Log L) { */ File sdkStorageRootDirectory = null; + protected ImmediateRequestGenerator immediateRequestGenerator = null; + // /** // * Maximum size of all string keys // */ diff --git a/sdk-java/src/main/java/ly/count/sdk/java/Countly.java b/sdk-java/src/main/java/ly/count/sdk/java/Countly.java index 3d26f980c..d337020d6 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/Countly.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/Countly.java @@ -2,8 +2,12 @@ import java.io.File; import java.util.Map; - -import ly.count.sdk.java.internal.*; +import ly.count.sdk.java.internal.CtxCore; +import ly.count.sdk.java.internal.Device; +import ly.count.sdk.java.internal.InternalConfig; +import ly.count.sdk.java.internal.Log; +import ly.count.sdk.java.internal.ModuleBackendMode; +import ly.count.sdk.java.internal.SDKCore; /** * Main Countly SDK API class. diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/DefaultNetworking.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/DefaultNetworking.java index 86c5a5b32..85451fbf8 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/DefaultNetworking.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/DefaultNetworking.java @@ -69,4 +69,9 @@ public void stop(CtxCore ctx) { shutdown = true; tasks.shutdown(); } + + @Override + public Transport getTransport() { + return transport; + } } diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ImmediateRequestGenerator.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ImmediateRequestGenerator.java new file mode 100644 index 000000000..5f2d2a466 --- /dev/null +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ImmediateRequestGenerator.java @@ -0,0 +1,17 @@ +package ly.count.sdk.java.internal; + +/** + * Interface for creating ImmediateRequestMaker + * a basic factory pattern implementation + * + * @see ImmediateRequestI + */ +public interface ImmediateRequestGenerator { + /** + * Create a new instance of ImmediateRequestI + * and override when needed + * + * @return new instance of ImmediateRequestI + */ + ImmediateRequestI createImmediateRequestMaker(); +} diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ImmediateRequestI.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ImmediateRequestI.java new file mode 100644 index 000000000..c25cad2e1 --- /dev/null +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ImmediateRequestI.java @@ -0,0 +1,17 @@ +package ly.count.sdk.java.internal; + +public interface ImmediateRequestI { + + /** + * Do the work of making the request directly without waiting for the queue. + * + * @param requestData query parameters + * @param customEndpoint custom endpoint to use instead of the default one + * @param cp transport to use when making the request + * @param requestShouldBeDelayed whether the request should be delayed or not + * @param networkingIsEnabled whether networking is enabled or not + * @param callback callback to call when the request is done + * @param log logger to use + */ + void doWork(String requestData, String customEndpoint, Transport cp, boolean requestShouldBeDelayed, boolean networkingIsEnabled, ImmediateRequestMaker.InternalImmediateRequestCallback callback, Log log); +} diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ImmediateRequestMaker.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ImmediateRequestMaker.java new file mode 100644 index 000000000..8d2edcd65 --- /dev/null +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ImmediateRequestMaker.java @@ -0,0 +1,116 @@ +package ly.count.sdk.java.internal; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.util.concurrent.CompletableFuture; +import org.json.JSONObject; + +/** + * Async task for making immediate server requests + */ +class ImmediateRequestMaker implements ImmediateRequestI { + + @Override + public void doWork(String requestData, String customEndpoint, Transport cp, boolean requestShouldBeDelayed, boolean networkingIsEnabled, InternalImmediateRequestCallback callback, Log log) { + CompletableFuture.supplyAsync(() -> doInBackground(requestData, customEndpoint, cp, requestShouldBeDelayed, networkingIsEnabled, callback, log)) + .thenAcceptAsync(this::onFinished); + } + + /** + * Used for callback from async task + */ + protected interface InternalImmediateRequestCallback { + void callback(JSONObject checkResponse); + } + + private InternalImmediateRequestCallback callback; + private Log L; + + /** + * params fields: + * 0 - requestData + * 1 - custom endpoint + * 2 - connection processor + * 3 - requestShouldBeDelayed + * 4 - networkingIsEnabled + * 5 - callback + * 6 - log module + */ + private JSONObject doInBackground(String requestData, String customEndpoint, Transport cp, boolean requestShouldBeDelayed, boolean networkingIsEnabled, InternalImmediateRequestCallback callback, Log log) { + this.callback = callback; + L = log; + if (!networkingIsEnabled) { + L.w("[ImmediateRequestMaker] ImmediateRequestMaker, Networking config is disabled, request cancelled. Endpoint[" + customEndpoint + "] request[" + requestData + "]"); + + return null; + } + + L.v("[ImmediateRequestMaker] Starting request"); + + HttpURLConnection connection = null; + + try { + L.d("[ImmediateRequestMaker] delayed[" + requestShouldBeDelayed + "] hasCallback[" + (callback != null) + "] endpoint[" + customEndpoint + "] request[" + requestData + "]"); + + if (requestShouldBeDelayed) { + //used in cases after something has to be done after a device id change + L.v("[ImmediateRequestMaker] request should be delayed, waiting for 0.5 seconds"); + + try { + Thread.sleep(500); + } catch (InterruptedException e) { + L.w("[ImmediateRequestMaker] While waiting for 0.5 seconds in ImmediateRequestMaker, sleep was interrupted"); + } + } + Request request = new Request(); + request.params.add(requestData); + request.endpoint(customEndpoint); + //getting connection ready + try { + connection = cp.connection(request, null); + } catch (IOException e) { + L.e("[ImmediateRequestMaker] IOException while preparing remote config update request :[" + e + "]"); + return null; + } + //connecting + connection.connect(); + + int code = connection.getResponseCode(); + String receivedBuffer = cp.response(connection); + + if (receivedBuffer == null) { + L.e("[ImmediateRequestMaker] Encountered problem while making a immediate server request, received result was null"); + return null; + } + + char firstChar = receivedBuffer.trim().charAt(0); + if (code >= 200 && code < 300 && (firstChar == '[' || receivedBuffer.contains("result"))) { + L.d("[ImmediateRequestMaker] Received the following response, :[" + receivedBuffer + "]"); + + // we check if the result was a json array or json object and convert the array into an object if necessary + if (firstChar == '[') { + return new JSONObject("{\"jsonArray\":" + receivedBuffer + "}"); + } + return new JSONObject(receivedBuffer); + } else { + L.e("[ImmediateRequestMaker] Encountered problem while making a immediate server request, :[" + receivedBuffer + "]"); + return null; + } + } catch (Exception e) { + L.e("[ImmediateRequestMaker] Received exception while making a immediate server request " + e.getMessage()); + } finally { + if (connection != null) { + connection.disconnect(); + } + } + L.v("[ImmediateRequestMaker] Finished request"); + return null; + } + + private void onFinished(JSONObject result) { + L.v("[ImmediateRequestMaker] onPostExecute"); + if (callback != null) { + callback.callback(result); + } + } +} \ No newline at end of file diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/InternalConfig.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/InternalConfig.java index 2adfa0745..c65efe240 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/InternalConfig.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/InternalConfig.java @@ -13,7 +13,6 @@ import java.util.List; import java.util.Map; import java.util.Set; - import ly.count.sdk.java.Config; /** @@ -34,6 +33,8 @@ public final class InternalConfig extends Config implements Storable { */ private final List dids = new ArrayList<>(); + ImmediateRequestGenerator immediateRequestGenerator = null; + /** * Shouldn't be used! */ diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/Networking.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/Networking.java index 4d659d1b9..965976a47 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/Networking.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/Networking.java @@ -8,4 +8,6 @@ public interface Networking { boolean check(CtxCore ctx); void stop(CtxCore ctx); + + Transport getTransport(); } diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKCore.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKCore.java index e5324b7cb..fb5b6be98 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKCore.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKCore.java @@ -1,10 +1,14 @@ package ly.count.sdk.java.internal; -import ly.count.sdk.java.Config; -import org.json.JSONObject; - -import java.util.*; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.TreeMap; import java.util.concurrent.Future; +import ly.count.sdk.java.Config; public class SDKCore { @@ -89,6 +93,10 @@ public boolean isTracking(Integer feat) { } public void init(CtxCore ctx) { + InternalConfig config = ctx.getConfig(); + if (config.immediateRequestGenerator == null) { + config.immediateRequestGenerator = ImmediateRequestMaker::new; + } prepareMappings(ctx); } diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/Transport.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/Transport.java index 809ec3966..f431d1349 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/Transport.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/Transport.java @@ -30,13 +30,11 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.Future; - import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; - import ly.count.sdk.java.User; import org.json.JSONObject; @@ -68,8 +66,8 @@ public Transport() { } /** - * @param config - * @throws IllegalArgumentException + * @param config configuration to use + * @throws IllegalArgumentException if certificate exception happens * @see ModuleBase#init(InternalConfig, Log) */ public void init(InternalConfig config, Log logger) throws IllegalArgumentException { @@ -93,13 +91,6 @@ public void init(InternalConfig config, Log logger) throws IllegalArgumentExcept } } - // void onContext(android.content.Ctx context) { - // ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(android.content.Ctx.CONNECTIVITY_SERVICE); - // NetworkInfo activeNetInfo = connectivityManager.getActiveNetworkInfo(); - // NetworkInfo mobNetInfo = connectivityManager.getNetworkInfo( ConnectivityManager.TYPE_MOBILE ); - // https://stackoverflow.com/questions/1783117/network-listener-android - // } - /** * For testing purposes */ @@ -232,6 +223,7 @@ void addMultipart(OutputStream output, PrintWriter writer, String boundary, Stri } byte[] pictureData(User user, String picture) throws IOException { + if (user == null) return null; byte[] data; if (UserEditorImpl.PICTURE_IN_USER_PROFILE.equals(picture)) { data = user.picture(); @@ -472,7 +464,7 @@ public void checkClientTrusted(X509Certificate[] chain, String authType) throws @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { - if (keyPins.size() == 0 && certPins.size() == 0) { + if (keyPins.isEmpty() && certPins.isEmpty()) { return; } @@ -493,8 +485,8 @@ public void checkServerTrusted(X509Certificate[] chain, String authType) throws defaultTrustManager.checkServerTrusted(chain, authType); } - byte serverPublicKey[] = chain[0].getPublicKey().getEncoded(); - byte serverCertificate[] = chain[0].getEncoded(); + byte[] serverPublicKey = chain[0].getPublicKey().getEncoded(); + byte[] serverCertificate = chain[0].getEncoded(); for (byte[] key : keyPins) { if (Arrays.equals(key, serverPublicKey)) { diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/BackendModeTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/BackendModeTests.java index 289771702..becdeadd5 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/BackendModeTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/BackendModeTests.java @@ -958,4 +958,4 @@ private int getHourFromTimeStamp(long timeStamp) { return calendar.get(Calendar.HOUR_OF_DAY); } -} +} \ No newline at end of file diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/ImmediateRequestTest.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/ImmediateRequestTest.java new file mode 100644 index 000000000..c479c25a7 --- /dev/null +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/ImmediateRequestTest.java @@ -0,0 +1,84 @@ +package ly.count.sdk.java.internal; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; +import ly.count.sdk.java.Countly; +import org.json.JSONObject; +import org.junit.After; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import static org.mockito.Mockito.mock; + +@RunWith(JUnit4.class) +public class ImmediateRequestTest { + Log L = mock(Log.class); + + @After + public void stop() { + Countly.stop(true); + } + + /** + * Immediate request maker "doWork" function + * Immediate Request Generator is default and endpoint and data are not valid, and app key, server url is default + * should return null because response is not okay + * + * @throws InterruptedException if thread is interrupted + */ + @Test + public void doWork_null() throws InterruptedException { + Countly.instance().init(TestUtils.getBaseConfig()); + AtomicReference callbackResult = new AtomicReference<>(true); + + ImmediateRequestMaker immediateRequestMaker = new ImmediateRequestMaker(); + immediateRequestMaker.doWork("test_event", "/o?", SDKCore.instance.networking.getTransport(), false, true, + (result) -> { + if (result == null) { + callbackResult.set(false); + } + }, L); + + Thread.sleep(2000); // wait for background thread to finish + Assert.assertFalse(callbackResult.get()); // check if callback was called and response is null + } + + /** + * Immediate request maker "doWork" function + * Immediate Request Generator is override and returns desired value + * should return desired value + * + * @throws InterruptedException if thread is interrupted + */ + @Test + public void doWork() throws InterruptedException { + Countly.instance().init(TestUtils.getBaseConfig()); + + JSONObject requestResult = new JSONObject(); + requestResult.put("result", "Success"); + requestResult.put("count", 6); + + ImmediateRequestI requestMaker = (requestData, customEndpoint, cp, requestShouldBeDelayed, networkingIsEnabled, callback, log) -> { + Assert.assertEquals("test_event", requestData); + Assert.assertEquals("/o?", customEndpoint); + Assert.assertTrue(networkingIsEnabled); + Assert.assertFalse(requestShouldBeDelayed); + callback.callback(requestResult); + }; + + SDKCore.instance.config.immediateRequestGenerator = () -> requestMaker; + ImmediateRequestI requestMakerGenerated = SDKCore.instance.config.immediateRequestGenerator.createImmediateRequestMaker(); + + AtomicInteger reqValidator = new AtomicInteger(0); + requestMakerGenerated.doWork("test_event", "/o?", SDKCore.instance.networking.getTransport(), false, true, + (result) -> { + Assert.assertEquals(requestResult, result); + reqValidator.set(requestResult.getInt("count")); + }, L); + + Thread.sleep(2000); // wait for background thread to finish + Assert.assertEquals(6, reqValidator.get()); // check if callback was called and response is null + } +} diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java index 502466a48..b44bbdc2c 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java @@ -10,6 +10,7 @@ import java.util.Map; import java.util.Scanner; import java.util.stream.Stream; +import ly.count.sdk.java.Config; import static ly.count.sdk.java.internal.SDKStorage.EVENT_QUEUE_FILE_NAME; import static ly.count.sdk.java.internal.SDKStorage.FILE_NAME_PREFIX; @@ -17,11 +18,45 @@ public class TestUtils { - private static String DELIMETER = ":::"; + static String DELIMETER = ":::"; + static String SERVER_URL = "https://test.count.ly"; + static String SERVER_APP_KEY = "COUNTLY_APP_KEY"; + static String DEVICE_ID = "some_random_test_device_id"; private TestUtils() { } + static Config getBaseConfig() { + File sdkStorageRootDirectory = getSdkStorageRootDirectory(); + checkSdkStorageRootDirectoryExist(sdkStorageRootDirectory); + Config config = new Config(SERVER_URL, SERVER_APP_KEY, sdkStorageRootDirectory); + config.setCustomDeviceId(DEVICE_ID); + + return config; + } + + static Config getVariantConfig(ImmediateRequestGenerator generator) { + Config config = getBaseConfig(); + InternalConfig internalConfig = new InternalConfig(config); + + internalConfig.immediateRequestGenerator = generator; + return config; + } + + static File getSdkStorageRootDirectory() { + // System specific folder structure + String[] sdkStorageRootPath = { System.getProperty("user.home"), "__COUNTLY", "java_test" }; + return new File(String.join(File.separator, sdkStorageRootPath)); + } + + static void checkSdkStorageRootDirectoryExist(File directory) { + if (!(directory.exists() && directory.isDirectory())) { + if (!directory.mkdirs()) { + throw new RuntimeException("Directory creation failed"); + } + } + } + /** * Get current request queue from target folder * @@ -80,7 +115,6 @@ protected static List getCurrentEventQueue(File targetFolder, Log log //do nothing } - //EventImplQueue.DELIMITER Arrays.stream(fileContent.split(DELIMETER)).forEach(s -> { final EventImpl event = EventImpl.fromJSON(s, (ev) -> { }, logger);