diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5fde8dd7..1711ae4b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,17 +14,17 @@ jobs: checks: write contents: read steps: - - uses: actions/checkout@v4 - - uses: actions/setup-java@v4 + - uses: actions/checkout@v6 + - uses: actions/setup-java@v5 with: java-version: '17' distribution: 'temurin' cache: maven - run: mvn -B verify - - uses: madrapps/jacoco-report@v1.6.1 + - uses: madrapps/jacoco-report@v1.7.2 with: paths: ${{ github.workspace }}/target/site/jacoco/jacoco.xml token: ${{ secrets.GITHUB_TOKEN }} min-coverage-overall: 80 min-coverage-changed-files: 80 - - uses: scacap/action-surefire-report@v1.7.3 \ No newline at end of file + - uses: scacap/action-surefire-report@v1.9.1 \ No newline at end of file diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index f6ad8c52..c00ed329 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -23,14 +23,14 @@ jobs: language: [ 'java' ] steps: - - uses: actions/checkout@v4 - - uses: actions/setup-java@v4 + - uses: actions/checkout@v6 + - uses: actions/setup-java@v5 with: java-version: '17' distribution: 'temurin' cache: maven - - uses: github/codeql-action/init@v3 + - uses: github/codeql-action/init@v4 with: languages: ${{ matrix.language }} - - uses: github/codeql-action/autobuild@v3 - - uses: github/codeql-action/analyze@v3 + - uses: github/codeql-action/autobuild@v4 + - uses: github/codeql-action/analyze@v4 diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 44316aef..ace69696 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -14,13 +14,13 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/configure-pages@v4 + - uses: actions/checkout@v6 + - uses: actions/configure-pages@v5 - uses: actions/jekyll-build-pages@v1 with: source: ./ destination: ./_site - - uses: actions/upload-pages-artifact@v3 + - uses: actions/upload-pages-artifact@v4 deploy: environment: name: github-pages diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 69ce43c6..3ab20fb0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,8 +5,8 @@ jobs: publish: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-java@v4 + - uses: actions/checkout@v6 + - uses: actions/setup-java@v5 with: java-version: '17' distribution: 'temurin' @@ -28,7 +28,7 @@ jobs: OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - - uses: release-drafter/release-drafter@v6 + - uses: release-drafter/release-drafter@v7 with: version: ${{ steps.version.outputs.version }} publish: true diff --git a/README.md b/README.md index bcd72ba4..db941103 100644 --- a/README.md +++ b/README.md @@ -69,13 +69,22 @@ Projects that use Matomo Java Tracker: ## What Is New? +### Version 3.4.x + +We fixed a synchronization issue in the Java 8 sender (https://github.com/matomo-org/matomo-java-tracker/issues/168). +To consume the exact amount of space needed for the queries to send to Matomo, we need the collection size of the incoming +requests. So we changed `Iterable` to `Collection` in some `MatomoTracker`. This could affect users, that use parameters +of type `Iterable` in the tracker. Please use `Collection` instead. + +### Version 3.3.x + Do you still use Matomo Java Tracker 2.x? We created version 3, that is compatible with Matomo 4 and 5 and contains fewer dependencies. Release notes can be found here: https://github.com/matomo-org/matomo-java-tracker/releases Here are the most important changes: -* Matomo Java Tracker 3.2.0 is compatible with Matomo 4 and 5 +* Matomo Java Tracker 3.4.0 is compatible with Matomo 4 and 5 * less dependencies * new dimension parameter * special types allow to provide valid parameters now @@ -140,7 +149,7 @@ Add a dependency on Matomo Java Tracker using Maven. For Java 8: org.piwik.java.tracking matomo-java-tracker - 3.2.0 + 3.4.0 ``` @@ -151,7 +160,7 @@ For Java 11: org.piwik.java.tracking matomo-java-tracker-java11 - 3.2.0 + 3.4.0 ``` @@ -159,7 +168,7 @@ or Gradle (Java 8): ```groovy dependencies { - implementation("org.piwik.java.tracking:matomo-java-tracker:3.2.0") + implementation("org.piwik.java.tracking:matomo-java-tracker:3.4.0") } ``` @@ -167,20 +176,20 @@ or Gradle (Java 11): ```groovy dependencies { - implementation("org.piwik.java.tracking:matomo-java-tracker-java11:3.2.0") + implementation("org.piwik.java.tracking:matomo-java-tracker-java11:3.4.0") } ``` or Gradle with Kotlin DSL (Java 8) ```kotlin -implementation("org.piwik.java.tracking:matomo-java-tracker:3.2.0") +implementation("org.piwik.java.tracking:matomo-java-tracker:3.4.0") ``` or Gradle with Kotlin DSL (Java 11) ```kotlin -implementation("org.piwik.java.tracking:matomo-java-tracker-java11:3.2.0") +implementation("org.piwik.java.tracking:matomo-java-tracker-java11:3.4.0") ``` ### Spring Boot Module @@ -193,7 +202,7 @@ and allows you to configure the tracker via application properties. Add the foll org.piwik.java.tracking matomo-java-tracker-spring-boot-starter - 3.2.0 + 3.4.0 ``` @@ -201,14 +210,14 @@ or Gradle: ```groovy dependencies { - implementation("org.piwik.java.tracking:matomo-java-tracker-spring-boot-starter:3.2.0") + implementation("org.piwik.java.tracking:matomo-java-tracker-spring-boot-starter:3.4.0") } ``` or Gradle with Kotlin DSL ```kotlin -implementation("org.piwik.java.tracking:matomo-java-tracker-spring-boot-starter:3.2.0") +implementation("org.piwik.java.tracking:matomo-java-tracker-spring-boot-starter:3.4.0") ``` The following properties are supported: @@ -270,14 +279,21 @@ To let the Matomo Java Tracker send a request to the Matomo instance, you need t ```java import java.net.URI; -import org.matomo.java.tracking.MatomoRequest; import org.matomo.java.tracking.MatomoRequests; import org.matomo.java.tracking.MatomoTracker; import org.matomo.java.tracking.TrackerConfiguration; import org.matomo.java.tracking.parameters.VisitorId; +/** + * Example for sending a request. + */ public class SendExample { + /** + * Example for sending a request. + * + * @param args ignored + */ public static void main(String[] args) { TrackerConfiguration configuration = TrackerConfiguration @@ -288,17 +304,19 @@ public class SendExample { .logFailedTracking(true) .build(); - MatomoTracker tracker = new MatomoTracker(configuration); - - tracker.sendRequestAsync(MatomoRequests - .event("Training", "Workout completed", "Bench press", 60.0) - .visitorId(VisitorId.fromString("customer@mail.com")) - .build() - ); - + try (MatomoTracker tracker = new MatomoTracker(configuration)) { + tracker.sendRequestAsync(MatomoRequests + .event("Training", "Workout completed", "Bench press", 60.0) + .visitorId(VisitorId.fromString("customer@mail.com")) + .build() + ); + } catch (Exception e) { + throw new RuntimeException("Could not close tracker", e); + } } } + ``` This will send a request to the Matomo instance at https://www.yourdomain.com/matomo.php and track a page view for the @@ -311,15 +329,23 @@ If you want to perform an operation after a successful asynchronous call to Mato result like this: ```java - import java.net.URI; +import org.matomo.java.tracking.MatomoRequest; import org.matomo.java.tracking.MatomoRequests; import org.matomo.java.tracking.MatomoTracker; import org.matomo.java.tracking.TrackerConfiguration; import org.matomo.java.tracking.parameters.VisitorId; +/** + * Example for sending a request and performing an action when the request was sent successfully. + */ public class ConsumerExample { + /** + * Example for sending a request and performing an action when the request was sent successfully. + * + * @param args ignored + */ public static void main(String[] args) { TrackerConfiguration configuration = TrackerConfiguration @@ -330,24 +356,27 @@ public class ConsumerExample { .logFailedTracking(true) .build(); - MatomoTracker tracker = new MatomoTracker(configuration); - - MatomoRequest request = MatomoRequests - .event("Training", "Workout completed", "Bench press", 60.0) - .visitorId(VisitorId.fromString("customer@mail.com")) - .build(); - - tracker.sendRequestAsync(request) - .thenAccept(req -> System.out.printf("Sent request %s%n", req)) - .exceptionally(throwable -> { - System.err.printf("Failed to send request: %s%n", throwable.getMessage()); - return null; - }); + try (MatomoTracker tracker = new MatomoTracker(configuration)) { + MatomoRequest request = MatomoRequests + .event("Training", "Workout completed", "Bench press", 60.0) + .visitorId(VisitorId.fromString("customer@mail.com")) + .build(); + + tracker.sendRequestAsync(request) + .thenAccept(req -> System.out.printf("Sent request %s%n", req)) + .exceptionally(throwable -> { + System.err.printf("Failed to send request: %s%n", throwable.getMessage()); + return null; + }); + } catch (Exception e) { + throw new RuntimeException("Could not close tracker", e); + } } } + ``` If you have multiple requests to wish to track, it may be more efficient to send them in a single HTTP call. To do this, @@ -355,14 +384,21 @@ send a bulk request. Place your requests in an _Iterable_ data structure and cal ```java import java.net.URI; -import org.matomo.java.tracking.MatomoRequest; import org.matomo.java.tracking.MatomoRequests; import org.matomo.java.tracking.MatomoTracker; import org.matomo.java.tracking.TrackerConfiguration; import org.matomo.java.tracking.parameters.VisitorId; +/** + * Example for sending multiple requests in one bulk request. + */ public class BulkExample { + /** + * Example for sending multiple requests in one bulk request. + * + * @param args ignored + */ public static void main(String[] args) { TrackerConfiguration configuration = TrackerConfiguration @@ -373,23 +409,24 @@ public class BulkExample { .logFailedTracking(true) .build(); - MatomoTracker tracker = new MatomoTracker(configuration); - - VisitorId visitorId = VisitorId.fromString("customer@mail.com"); - tracker.sendBulkRequestAsync( - MatomoRequests.siteSearch("Running shoes", "Running", 120L) - .visitorId(visitorId).build(), - MatomoRequests.pageView("VelocityStride ProX Running Shoes") - .visitorId(visitorId).build(), - MatomoRequests.ecommerceOrder("QXZ-789LMP", 100.0, 124.0, 19.0, 10.0, 5.0) - .visitorId(visitorId) - .build() - ); + try (MatomoTracker tracker = new MatomoTracker(configuration)) { + VisitorId visitorId = VisitorId.fromString("customer@mail.com"); + tracker.sendBulkRequestAsync( + MatomoRequests.siteSearch("Running shoes", "Running", 120L) + .visitorId(visitorId).build(), + MatomoRequests.pageView("VelocityStride ProX Running Shoes") + .visitorId(visitorId).build(), + MatomoRequests.ecommerceOrder("QXZ-789LMP", 100.0, 124.0, 19.0, 10.0, 5.0) + .visitorId(visitorId) + .build() + ); + } catch (Exception e) { + throw new RuntimeException("Could not close tracker", e); + } } } - ``` This will send two requests in a single HTTP call. The requests will be sent asynchronously. @@ -410,9 +447,7 @@ a unique identifier, e.g. an email address. If you do not provide a visitor id, Ecommerce requests contain ecommerce items, that can be fluently build: ```java - import java.net.URI; -import org.matomo.java.tracking.MatomoRequest; import org.matomo.java.tracking.MatomoRequests; import org.matomo.java.tracking.MatomoTracker; import org.matomo.java.tracking.TrackerConfiguration; @@ -420,8 +455,16 @@ import org.matomo.java.tracking.parameters.EcommerceItem; import org.matomo.java.tracking.parameters.EcommerceItems; import org.matomo.java.tracking.parameters.VisitorId; +/** + * Example for sending an ecommerce request. + */ public class EcommerceExample { + /** + * Example for sending an ecommerce request. + * + * @param args ignored + */ public static void main(String[] args) { TrackerConfiguration configuration = TrackerConfiguration @@ -432,37 +475,38 @@ public class EcommerceExample { .logFailedTracking(true) .build(); - MatomoTracker tracker = new MatomoTracker(configuration); - - tracker.sendBulkRequestAsync(MatomoRequests - .ecommerceCartUpdate(50.0) - .ecommerceItems(EcommerceItems - .builder() - .item(EcommerceItem - .builder() - .sku("XYZ12345") - .name("Matomo - The big book about web analytics") - .category("Education & Teaching") - .price(23.1) - .quantity(2) - .build()) - .item(EcommerceItem - .builder() - .sku("B0C2WV3MRJ") - .name("Matomo for data visualization") - .category("Education & Teaching") - .price(15.0) - .quantity(1) - .build()) - .build()) - .visitorId(VisitorId.fromString("customer@mail.com")) - .build() - ); + try (MatomoTracker tracker = new MatomoTracker(configuration)) { + tracker.sendBulkRequestAsync(MatomoRequests + .ecommerceCartUpdate(50.0) + .ecommerceItems(EcommerceItems + .builder() + .item(EcommerceItem + .builder() + .sku("XYZ12345") + .name("Matomo - The big book about web analytics") + .category("Education & Teaching") + .price(23.1) + .quantity(2) + .build()) + .item(EcommerceItem + .builder() + .sku("B0C2WV3MRJ") + .name("Matomo for data visualization") + .category("Education & Teaching") + .price(15.0) + .quantity(1) + .build()) + .build()) + .visitorId(VisitorId.fromString("customer@mail.com")) + .build() + ); + } catch (Exception e) { + throw new RuntimeException("Could not close tracker", e); + } } } - ``` Note that if you want to be able to track campaigns using *Referrers > Campaigns*, you must add the correct @@ -640,11 +684,10 @@ the Maven goal `install, a snapshot version can be used in your local Maven repository for testing purposes, e.g. ```xml - org.piwik.java.tracking matomo-java-tracker - 3.2.1-SNAPSHOT + 3.4.1-SNAPSHOT ``` diff --git a/core/pom.xml b/core/pom.xml index b6d57050..a716cbbb 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.2-SNAPSHOT + 3.4.1-SNAPSHOT ../pom.xml diff --git a/core/src/main/java/org/matomo/java/tracking/ExecutorServiceCloser.java b/core/src/main/java/org/matomo/java/tracking/ExecutorServiceCloser.java new file mode 100644 index 00000000..1ef11c97 --- /dev/null +++ b/core/src/main/java/org/matomo/java/tracking/ExecutorServiceCloser.java @@ -0,0 +1,42 @@ +package org.matomo.java.tracking; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; +import lombok.NonNull; + +/** + * Helps to close an executor service. + */ +public class ExecutorServiceCloser { + + /** + * Closes the given executor service. + * + *

This will check whether the executor service is already terminated, and if not, it + * initiates a shutdown and waits a minute. If the minute expires, the executor service + * is shutdown immediately. + * + * @param executorService The executor service to close + */ + public static void close(@NonNull ExecutorService executorService) { + boolean terminated = executorService.isTerminated(); + if (!terminated) { + executorService.shutdown(); + boolean interrupted = false; + while (!terminated) { + try { + terminated = executorService.awaitTermination(1L, TimeUnit.MINUTES); + } catch (InterruptedException e) { + if (!interrupted) { + executorService.shutdownNow(); + interrupted = true; + } + } + } + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + +} diff --git a/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java b/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java index ea2ddbfc..42ef0c29 100644 --- a/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java +++ b/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java @@ -355,7 +355,7 @@ public class MatomoRequest { /** * The grand total for the ecommerce order (required when tracking an ecommerce order). */ - @TrackingParameter(name = "revenue") + @TrackingParameter(name = "revenue", min = 0) private Double ecommerceRevenue; /** @@ -433,7 +433,7 @@ public class MatomoRequest { /** * Some numeric value that represents the event value. */ - @TrackingParameter(name = "e_n") + @TrackingParameter(name = "e_v", min = 0) private Double eventValue; /** @@ -476,25 +476,25 @@ public class MatomoRequest { /** * The subtotal of the order; excludes shipping. */ - @TrackingParameter(name = "ec_st") + @TrackingParameter(name = "ec_st", min = 0) private Double ecommerceSubtotal; /** * Tax amount of the order. */ - @TrackingParameter(name = "ec_tx") + @TrackingParameter(name = "ec_tx", min = 0) private Double ecommerceTax; /** * Shipping cost of the order. */ - @TrackingParameter(name = "ec_sh") + @TrackingParameter(name = "ec_sh", min = 0) private Double ecommerceShippingCost; /** * Discount offered. */ - @TrackingParameter(name = "ec_dt") + @TrackingParameter(name = "ec_dt", min = 0) private Double ecommerceDiscount; /** diff --git a/core/src/main/java/org/matomo/java/tracking/MatomoTracker.java b/core/src/main/java/org/matomo/java/tracking/MatomoTracker.java index 51a585d5..3c38d017 100644 --- a/core/src/main/java/org/matomo/java/tracking/MatomoTracker.java +++ b/core/src/main/java/org/matomo/java/tracking/MatomoTracker.java @@ -11,6 +11,7 @@ import java.net.URI; import java.time.Duration; import java.util.Arrays; +import java.util.Collection; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; import java.util.function.Function; @@ -35,7 +36,7 @@ * @author brettcsorba */ @Slf4j -public class MatomoTracker { +public class MatomoTracker implements AutoCloseable { private final TrackerConfiguration trackerConfiguration; @@ -135,7 +136,7 @@ public MatomoTracker( * *

Use this method if you want to send a single request. If you want to send multiple requests at once, use * {@link #sendBulkRequest(Iterable)} instead. If you want to send multiple requests asynchronously, use - * {@link #sendRequestAsync(MatomoRequest)} or {@link #sendBulkRequestAsync(Iterable)} instead. + * {@link #sendRequestAsync(MatomoRequest)} or {@link #sendBulkRequestAsync(Collection)} instead. * * @param request request to send. must not be null */ @@ -160,7 +161,7 @@ private void initializeSender() { * Send a request asynchronously via HTTP GET. * *

Use this method if you want to send a single request. If you want to send multiple requests at once, use - * {@link #sendBulkRequestAsync(Iterable)} instead. If you want to send multiple requests synchronously, use + * {@link #sendBulkRequestAsync(Collection)} instead. If you want to send multiple requests synchronously, use * {@link #sendRequest(MatomoRequest)} or {@link #sendBulkRequest(Iterable)} instead. * * @param request request to send @@ -176,8 +177,8 @@ public CompletableFuture sendRequestAsync( * Send a request asynchronously via HTTP GET and specify a callback that gets executed when the response arrives. * *

Use this method if you want to send a single request. If you want to send multiple requests at once, use - * {@link #sendBulkRequestAsync(Iterable, Consumer)} instead. If you want to send multiple requests synchronously, use - * {@link #sendRequest(MatomoRequest)} or {@link #sendBulkRequest(Iterable)} instead. + * {@link #sendBulkRequestAsync(Collection, Consumer)} instead. If you want to send multiple requests synchronously, + * use {@link #sendRequest(MatomoRequest)} or {@link #sendBulkRequest(Iterable)} instead. * * @param request request to send * @param callback callback that gets executed when response arrives, must not be null @@ -223,7 +224,7 @@ private void applyGoalIdAndCheckSiteId( * *

More efficient than sending several individual requests. If you want to send a single request, use * {@link #sendRequest(MatomoRequest)} instead. If you want to send multiple requests asynchronously, use - * {@link #sendBulkRequestAsync(Iterable)} instead. + * {@link #sendBulkRequestAsync(Collection)} instead. * * @param requests the requests to send */ @@ -236,7 +237,7 @@ public void sendBulkRequest(MatomoRequest... requests) { * *

More efficient than sending several individual requests. If you want to send a single request, use * {@link #sendRequest(MatomoRequest)} instead. If you want to send multiple requests asynchronously, use - * {@link #sendBulkRequestAsync(Iterable)} instead. + * {@link #sendBulkRequestAsync(Collection)} instead. * * @param requests the requests to send */ @@ -250,7 +251,7 @@ public void sendBulkRequest(@NonNull Iterable requests) * *

Specify the AuthToken if parameters that require an auth token is used. If you want to send a single request, * use {@link #sendRequest(MatomoRequest)} instead. If you want to send multiple requests asynchronously, use - * {@link #sendBulkRequestAsync(Iterable)} instead. + * {@link #sendBulkRequestAsync(Collection)} instead. * * @param requests the requests to send * @param authToken specify if any of the parameters use require AuthToken, if null the default auth token from the @@ -293,7 +294,7 @@ public CompletableFuture sendBulkRequestAsync(MatomoRequest... requests) { * @return completable future to let you know when the request is done */ public CompletableFuture sendBulkRequestAsync( - @NonNull Iterable requests + @NonNull Collection requests ) { return sendBulkRequestAsync(requests, null, null); } @@ -313,7 +314,7 @@ public CompletableFuture sendBulkRequestAsync( */ @Deprecated public CompletableFuture sendBulkRequestAsync( - @NonNull Iterable requests, + @NonNull Collection requests, @Nullable String authToken, @Nullable Consumer callback ) { @@ -342,7 +343,7 @@ public CompletableFuture sendBulkRequestAsync( * @return completable future to let you know when the request is done */ public CompletableFuture sendBulkRequestAsync( - @NonNull Iterable requests, + @NonNull Collection requests, @Nullable Consumer callback ) { return sendBulkRequestAsync(requests, null, callback); @@ -357,11 +358,19 @@ public CompletableFuture sendBulkRequestAsync( * @param authToken specify if any of the parameters use require AuthToken, null allowed * @return completable future to let you know when the request is done * @deprecated Please set the auth token in the tracker configuration or the requests directly and use - * {@link #sendBulkRequestAsync(Iterable)} instead. + * {@link #sendBulkRequestAsync(Collection)} instead. */ public CompletableFuture sendBulkRequestAsync( - @NonNull Iterable requests, @Nullable String authToken + @NonNull Collection requests, @Nullable String authToken ) { return sendBulkRequestAsync(requests, authToken, null); } + + @Override + public void close() throws Exception { + if (sender != null) { + sender.close(); + } + } + } diff --git a/core/src/main/java/org/matomo/java/tracking/Sender.java b/core/src/main/java/org/matomo/java/tracking/Sender.java index acb82280..7b1df985 100644 --- a/core/src/main/java/org/matomo/java/tracking/Sender.java +++ b/core/src/main/java/org/matomo/java/tracking/Sender.java @@ -2,9 +2,10 @@ import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; +import java.util.Collection; import java.util.concurrent.CompletableFuture; -interface Sender { +interface Sender extends AutoCloseable { @NonNull CompletableFuture sendSingleAsync( @NonNull MatomoRequest request @@ -20,6 +21,6 @@ void sendBulk( @NonNull CompletableFuture sendBulkAsync( - @NonNull Iterable requests, @Nullable String overrideAuthToken + @NonNull Collection requests, @Nullable String overrideAuthToken ); } diff --git a/core/src/test/java/org/matomo/java/tracking/ExecutorServiceCloserTest.java b/core/src/test/java/org/matomo/java/tracking/ExecutorServiceCloserTest.java new file mode 100644 index 00000000..e0d167a9 --- /dev/null +++ b/core/src/test/java/org/matomo/java/tracking/ExecutorServiceCloserTest.java @@ -0,0 +1,46 @@ +package org.matomo.java.tracking; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import org.junit.jupiter.api.Test; + +class ExecutorServiceCloserTest { + + @Test + void shutsDownExecutorService() { + + ExecutorService executorService = Executors.newFixedThreadPool(2, new DaemonThreadFactory()); + + ExecutorServiceCloser.close(executorService); + + assertThat(executorService.isTerminated()).isTrue(); + assertThat(executorService.isShutdown()).isTrue(); + + } + + @Test + void shutsDownExecutorServiceImmediately() throws Exception { + + ExecutorService executorService = Executors.newSingleThreadExecutor(); + executorService.submit(() -> { + try { + Thread.sleep(10000L); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + }); + + Thread thread = new Thread(() -> { + ExecutorServiceCloser.close(executorService); + }); + thread.start(); + Thread.sleep(1000L); + thread.interrupt(); + + assertThat(executorService.isShutdown()).isTrue(); + + } + +} \ No newline at end of file diff --git a/core/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java b/core/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java index 1b1cecd4..a4e87e5b 100644 --- a/core/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java +++ b/core/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java @@ -538,4 +538,67 @@ void failsIfLongitudeIsGreaterThan180() { .hasRootCauseMessage("Invalid value for long. Must be less or equal than 180"); } -} + @Test + void tracksEvent() { + matomoRequestBuilder.eventName("Event Name") + .eventValue(23.456) + .eventAction("Event Action") + .eventCategory("Event Category"); + + whenCreatesQuery(); + + assertThat(query).isEqualTo("idsite=42&token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&e_c=Event+Category&e_a=Event+Action&e_n=Event+Name&e_v=23.456&send_image=0&rand=random-value"); + } + + @Test + void allowsZeroForEventValue() { + matomoRequestBuilder.eventName("Event Name") + .eventValue(0.0) + .eventAction("Event Action") + .eventCategory("Event Category"); + + whenCreatesQuery(); + + assertThat(query) + .isEqualTo("idsite=42&" + + "token_auth=876de1876fb2cda2816c362a61bfc712&" + + "rec=1&" + + "apiv=1&" + + "_id=112210f47de98115&" + + "e_c=Event+Category&" + + "e_a=Event+Action&" + + "e_n=Event+Name&" + + "e_v=0.0&" + + "send_image=0&" + + "rand=random-value" + ); + } + + @Test + void allowsZeroForEcommerceValues() { + matomoRequestBuilder + .ecommerceRevenue(0.0) + .ecommerceSubtotal(0.0) + .ecommerceTax(0.0) + .ecommerceShippingCost(0.0) + .ecommerceDiscount(0.0); + + whenCreatesQuery(); + + assertThat(query) + .isEqualTo("idsite=42&" + + "token_auth=876de1876fb2cda2816c362a61bfc712&" + + "rec=1&" + + "apiv=1&" + + "_id=112210f47de98115&" + + "revenue=0.0&" + + "ec_st=0.0&" + + "ec_tx=0.0&" + + "ec_sh=0.0&" + + "ec_dt=0.0&" + + "send_image=0&" + + "rand=random-value" + ); + } + +} \ No newline at end of file diff --git a/core/src/test/java/org/matomo/java/tracking/TestSender.java b/core/src/test/java/org/matomo/java/tracking/TestSender.java index 4d4c7465..f000bc8d 100644 --- a/core/src/test/java/org/matomo/java/tracking/TestSender.java +++ b/core/src/test/java/org/matomo/java/tracking/TestSender.java @@ -53,7 +53,7 @@ public void sendBulk( @NonNull @Override public CompletableFuture sendBulkAsync( - @NonNull Iterable requests, @Nullable String overrideAuthToken + @NonNull Collection requests, @Nullable String overrideAuthToken ) { for (MatomoRequest request : requests) { createQueryAndAddRequest(request, overrideAuthToken); @@ -69,4 +69,8 @@ private void createQueryAndAddRequest(@lombok.NonNull MatomoRequest request, @Nu requests.add(request); } + @Override + public void close() { + // do nothing + } } diff --git a/java11/pom.xml b/java11/pom.xml index f367934e..fb03eedf 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.2-SNAPSHOT + 3.4.1-SNAPSHOT ../pom.xml matomo-java-tracker-java11 - 3.2.2-SNAPSHOT + 3.4.1-SNAPSHOT jar Matomo Java Tracker Java 11 @@ -48,7 +48,7 @@ org.wiremock wiremock - 3.3.1 + 3.13.2 test diff --git a/java11/src/main/java/org/matomo/java/tracking/Java11Sender.java b/java11/src/main/java/org/matomo/java/tracking/Java11Sender.java index 3daf434f..abb6181f 100644 --- a/java11/src/main/java/org/matomo/java/tracking/Java11Sender.java +++ b/java11/src/main/java/org/matomo/java/tracking/Java11Sender.java @@ -17,6 +17,7 @@ import java.util.TreeMap; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -39,9 +40,14 @@ public class Java11Sender implements Sender { @lombok.NonNull private final CookieStore cookieStore; + @lombok.NonNull + private final ExecutorService executorService; + @NonNull @Override - public CompletableFuture sendSingleAsync(@NonNull @lombok.NonNull MatomoRequest request) { + public CompletableFuture sendSingleAsync( + @NonNull @lombok.NonNull MatomoRequest request + ) { return sendAsyncAndCheckResponse(buildHttpGetRequest(request), request); } @@ -51,14 +57,19 @@ public void sendSingle(@NonNull @lombok.NonNull MatomoRequest request) { } private void sendAndCheckResponse(@NonNull HttpRequest httpRequest) { - checkResponse(send(httpRequest, () -> httpClient.send(httpRequest, HttpResponse.BodyHandlers.discarding())), + checkResponse( + send( + httpRequest, + () -> httpClient.send(httpRequest, HttpResponse.BodyHandlers.discarding()) + ), httpRequest ); } @Override public void sendBulk( - @NonNull @lombok.NonNull Iterable requests, @Nullable String overrideAuthToken + @NonNull @lombok.NonNull Iterable requests, + @Nullable String overrideAuthToken ) { sendAndCheckResponse(buildHttpPostRequest(requests, overrideAuthToken)); } @@ -67,7 +78,8 @@ public void sendBulk( private HttpRequest buildHttpPostRequest( @NonNull Iterable requests, @Nullable String overrideAuthToken ) { - String authToken = AuthToken.determineAuthToken(overrideAuthToken, requests, trackerConfiguration); + String authToken = + AuthToken.determineAuthToken(overrideAuthToken, requests, trackerConfiguration); Collection queries = new ArrayList<>(); Map headers = new LinkedHashMap<>(10); String headerUserAgent = null; @@ -103,7 +115,8 @@ private HttpRequest buildHttpPostRequest( @NonNull @Override public CompletableFuture sendBulkAsync( - @NonNull @lombok.NonNull Iterable requests, @Nullable String overrideAuthToken + @NonNull @lombok.NonNull Collection requests, + @Nullable String overrideAuthToken ) { return sendAsyncAndCheckResponse(buildHttpPostRequest(requests, overrideAuthToken), null); } @@ -112,11 +125,13 @@ public CompletableFuture sendBulkAsync( private CompletableFuture sendAsyncAndCheckResponse( @NonNull HttpRequest httpRequest, @Nullable T result ) { - return send(httpRequest, - () -> httpClient.sendAsync(httpRequest, HttpResponse.BodyHandlers.discarding()).thenApply(response -> { - checkResponse(response, httpRequest); - return result; - }) + return send( + httpRequest, + () -> httpClient.sendAsync(httpRequest, HttpResponse.BodyHandlers.discarding()) + .thenApply(response -> { + checkResponse(response, httpRequest); + return result; + }) ); } @@ -129,7 +144,8 @@ private HttpRequest buildHttpGetRequest(@NonNull MatomoRequest request) { URI apiEndpoint = trackerConfiguration.getApiEndpoint(); HttpRequest.Builder builder = HttpRequest .newBuilder() - .uri(apiEndpoint.resolve(String.format("%s?%s", + .uri(apiEndpoint.resolve(String.format( + "%s?%s", apiEndpoint.getPath(), queryCreator.createQuery(request, authToken) ))); @@ -155,12 +171,22 @@ private T send( } } - private void checkResponse(@NonNull HttpResponse response, @NonNull HttpRequest httpRequest) { + private void checkResponse( + @NonNull HttpResponse response, + @NonNull HttpRequest httpRequest + ) { if (response.statusCode() > 399) { if (trackerConfiguration.isLogFailedTracking()) { - log.error("Received HTTP error code {} for URL {}", response.statusCode(), httpRequest.uri()); + log.error( + "Received HTTP error code {} for URL {}", + response.statusCode(), + httpRequest.uri() + ); } - throw new MatomoException(String.format("Tracking endpoint responded with code %d", response.statusCode())); + throw new MatomoException(String.format( + "Tracking endpoint responded with code %d", + response.statusCode() + )); } } @@ -176,13 +202,16 @@ private void addCookies(MatomoRequest request) { } private void applyTrackerConfiguration(@NonNull HttpRequest.Builder builder) { - if (trackerConfiguration.getSocketTimeout() != null && trackerConfiguration.getSocketTimeout().toMillis() > 0L) { + if (trackerConfiguration.getSocketTimeout() != null + && trackerConfiguration.getSocketTimeout().toMillis() > 0L) { builder.timeout(trackerConfiguration.getSocketTimeout()); } } private void setUserAgentHeader( - HttpRequest.Builder builder, @Nullable String headerUserAgent, @Nullable Map headers + HttpRequest.Builder builder, + @Nullable String headerUserAgent, + @Nullable Map headers ) { String userAgentHeader = null; if ((headerUserAgent == null || headerUserAgent.trim().isEmpty()) && headers != null) { @@ -197,11 +226,19 @@ private void setUserAgentHeader( } } - private void addHeaders(@NonNull HttpRequest.Builder builder, @Nullable Map headers) { + private void addHeaders( + @NonNull HttpRequest.Builder builder, + @Nullable Map headers + ) { if (headers != null) { for (Map.Entry header : headers.entrySet()) { builder.header(header.getKey(), header.getValue()); } } } + + @Override + public void close() { + ExecutorServiceCloser.close(executorService); + } } diff --git a/java11/src/main/java/org/matomo/java/tracking/Java11SenderProvider.java b/java11/src/main/java/org/matomo/java/tracking/Java11SenderProvider.java index 273ca19f..d6145198 100644 --- a/java11/src/main/java/org/matomo/java/tracking/Java11SenderProvider.java +++ b/java11/src/main/java/org/matomo/java/tracking/Java11SenderProvider.java @@ -6,6 +6,7 @@ import java.net.ProxySelector; import java.net.http.HttpClient; import java.security.SecureRandom; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; @@ -22,19 +23,25 @@ public Sender provideSender( TrackerConfiguration trackerConfiguration, QueryCreator queryCreator ) { CookieManager cookieManager = new CookieManager(); + ExecutorService executorService = Executors.newFixedThreadPool( + trackerConfiguration.getThreadPoolSize(), + new DaemonThreadFactory() + ); HttpClient.Builder builder = HttpClient .newBuilder() .cookieHandler(cookieManager) - .executor(Executors.newFixedThreadPool(trackerConfiguration.getThreadPoolSize(), new DaemonThreadFactory())) - ; - if (trackerConfiguration.getConnectTimeout() != null && trackerConfiguration.getConnectTimeout().toMillis() > 0L) { + .executor(executorService); + if (trackerConfiguration.getConnectTimeout() != null + && trackerConfiguration.getConnectTimeout().toMillis() > 0L) { builder.connectTimeout(trackerConfiguration.getConnectTimeout()); } if (!isEmpty(trackerConfiguration.getProxyHost()) && trackerConfiguration.getProxyPort() > 0) { - builder.proxy(ProxySelector.of(new InetSocketAddress(trackerConfiguration.getProxyHost(), + builder.proxy(ProxySelector.of(new InetSocketAddress( + trackerConfiguration.getProxyHost(), trackerConfiguration.getProxyPort() ))); - if (!isEmpty(trackerConfiguration.getProxyUsername()) && !isEmpty(trackerConfiguration.getProxyPassword())) { + if (!isEmpty(trackerConfiguration.getProxyUsername()) + && !isEmpty(trackerConfiguration.getProxyPassword())) { builder.authenticator(new ProxyAuthenticator( trackerConfiguration.getProxyUsername(), trackerConfiguration.getProxyPassword() @@ -54,7 +61,13 @@ public Sender provideSender( throw new MatomoException("Please disable SSL hostname verification manually using the system parameter -Djdk.internal.httpclient.disableHostnameVerification=true"); } - return new Java11Sender(trackerConfiguration, queryCreator, builder.build(), cookieManager.getCookieStore()); + return new Java11Sender( + trackerConfiguration, + queryCreator, + builder.build(), + cookieManager.getCookieStore(), + executorService + ); } private static boolean isEmpty( diff --git a/java11/src/test/java/org/matomo/java/tracking/Java11SenderIT.java b/java11/src/test/java/org/matomo/java/tracking/Java11SenderIT.java index cdfef1df..6ce1a63b 100644 --- a/java11/src/test/java/org/matomo/java/tracking/Java11SenderIT.java +++ b/java11/src/test/java/org/matomo/java/tracking/Java11SenderIT.java @@ -21,6 +21,7 @@ import java.time.Duration; import java.util.List; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executors; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -45,7 +46,8 @@ void failsIfTrackerConfigurationIsNotSet() { .apiEndpoint(URI.create("http://localhost")) .build()), HttpClient.newBuilder().cookieHandler(cookieManager).build(), - cookieManager.getCookieStore() + cookieManager.getCookieStore(), + Executors.newFixedThreadPool(2, new DaemonThreadFactory()) )).isInstanceOf(NullPointerException.class) .hasMessage("trackerConfiguration is marked non-null but is null"); } @@ -57,7 +59,8 @@ void failsIfQueryCreatorIsNotSet() { TrackerConfiguration.builder().apiEndpoint(URI.create("http://localhost")).build(), null, HttpClient.newBuilder().cookieHandler(cookieManager).build(), - cookieManager.getCookieStore() + cookieManager.getCookieStore(), + Executors.newFixedThreadPool(2, new DaemonThreadFactory()) )).isInstanceOf(NullPointerException.class) .hasMessage("queryCreator is marked non-null but is null"); } @@ -71,7 +74,8 @@ void failsIfHttpClientIsNotSet() { .apiEndpoint(URI.create("http://localhost")) .build()), null, - cookieManager.getCookieStore() + cookieManager.getCookieStore(), + Executors.newFixedThreadPool(2, new DaemonThreadFactory()) )).isInstanceOf(NullPointerException.class) .hasMessage("httpClient is marked non-null but is null"); } @@ -85,7 +89,8 @@ void failsIfCookieStoreIsNotSet() { .apiEndpoint(URI.create("http://localhost")) .build()), HttpClient.newBuilder().cookieHandler(cookieManager).build(), - null + null, + Executors.newFixedThreadPool(2, new DaemonThreadFactory()) )).isInstanceOf(NullPointerException.class) .hasMessage("cookieStore is marked non-null but is null"); } diff --git a/java8/pom.xml b/java8/pom.xml index 9c47baae..4b4621ad 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.2-SNAPSHOT + 3.4.1-SNAPSHOT ../pom.xml matomo-java-tracker - 3.2.2-SNAPSHOT + 3.4.1-SNAPSHOT jar Matomo Java Tracker Java 8 diff --git a/java8/src/main/java/org/matomo/java/tracking/Java8Sender.java b/java8/src/main/java/org/matomo/java/tracking/Java8Sender.java index 9b386f89..9aca209c 100644 --- a/java8/src/main/java/org/matomo/java/tracking/Java8Sender.java +++ b/java8/src/main/java/org/matomo/java/tracking/Java8Sender.java @@ -27,10 +27,11 @@ import java.util.Collection; import java.util.Iterator; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; @@ -59,8 +60,7 @@ class Java8Sender implements Sender { private final QueryCreator queryCreator; - private final Executor executor; - private final Collection queries = new ArrayList<>(16); + private final ExecutorService executorService; @Override @NonNull @@ -70,7 +70,7 @@ public CompletableFuture sendSingleAsync( return CompletableFuture.supplyAsync(() -> { sendSingle(request); return request; - }, executor); + }, executorService); } @Override @@ -322,39 +322,37 @@ private static void preparePostConnection(HttpURLConnection connection) { @Override @NonNull public CompletableFuture sendBulkAsync( - @NonNull Iterable requests, @Nullable String overrideAuthToken + @NonNull Collection requests, @Nullable String overrideAuthToken ) { String authToken = AuthToken.determineAuthToken(overrideAuthToken, requests, trackerConfiguration); Map headers = new LinkedHashMap<>(); String headerUserAgent = findHeaderUserAgent(requests); String sessionId = findSessionId(requests); Map cookies = findCookies(requests); - synchronized (queries) { - for (MatomoRequest request : requests) { - RequestValidator.validate(request, authToken); - if (request.getHeaders() != null && !request.getHeaders().isEmpty()) { - headers.putAll(request.getHeaders()); - } - String query = queryCreator.createQuery(request, null); - queries.add(query); + List queries = new ArrayList<>(requests.size()); + for (MatomoRequest request : requests) { + RequestValidator.validate(request, authToken); + if (request.getHeaders() != null && !request.getHeaders().isEmpty()) { + headers.putAll(request.getHeaders()); } + queries.add(queryCreator.createQuery(request, null)); } return CompletableFuture.supplyAsync(() -> - sendBulkAsync(authToken, headers, headerUserAgent, sessionId, cookies), executor); + sendBulkAsync(queries, authToken, headers, headerUserAgent, sessionId, cookies), + executorService); } @Nullable private Void sendBulkAsync( - @Nullable String authToken, Map headers, String headerUserAgent, String sessionId, + List queries, + @Nullable String authToken, + Map headers, + String headerUserAgent, + String sessionId, Map cookies ) { - synchronized (queries) { - if (!queries.isEmpty()) { - sendBulk(queries, authToken, headers, headerUserAgent, sessionId, cookies); - queries.clear(); - } - return null; - } + sendBulk(queries, authToken, headers, headerUserAgent, sessionId, cookies); + return null; } @Nullable @@ -385,4 +383,8 @@ private Map findCookies(Iterable reques return null; } + @Override + public void close() { + ExecutorServiceCloser.close(executorService); + } } diff --git a/java8/src/test/java/org/matomo/java/tracking/Java8SenderIT.java b/java8/src/test/java/org/matomo/java/tracking/Java8SenderIT.java index 4b92535f..dcaeafa4 100644 --- a/java8/src/test/java/org/matomo/java/tracking/Java8SenderIT.java +++ b/java8/src/test/java/org/matomo/java/tracking/Java8SenderIT.java @@ -16,6 +16,7 @@ import java.net.MalformedURLException; import java.net.URI; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executors; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -242,7 +243,7 @@ private void givenSender() { sender = new Java8Sender( trackerConfiguration, new QueryCreator(trackerConfiguration), - Runnable::run + Executors.newFixedThreadPool(2, new DaemonThreadFactory()) ); } diff --git a/java8/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java b/java8/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java index fe8eb7c1..04500061 100644 --- a/java8/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java +++ b/java8/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java @@ -23,6 +23,7 @@ import java.util.Locale.LanguageRange; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicBoolean; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -69,6 +70,13 @@ void givenStub() { wireMockServer.stubFor(get(urlPathEqualTo("/matomo.php")).willReturn(status(204))); } + @AfterEach + void closeTracker() throws Exception { + if (matomoTracker != null) { + matomoTracker.close(); + } + } + @Test void requiresSiteId() { diff --git a/pom.xml b/pom.xml index 247ff62f..f4c62914 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.2-SNAPSHOT + 3.4.1-SNAPSHOT pom Matomo Java Tracker Parent @@ -62,8 +62,8 @@ 1.8 1.8 ${project.build.outputDirectory}/delombok - 1.18.30 - 2.0.12 + 1.18.36 + 2.0.17 @@ -71,7 +71,7 @@ com.github.spotbugs spotbugs-annotations - 4.8.3 + 4.9.8 provided @@ -88,13 +88,13 @@ org.junit.jupiter junit-jupiter - 5.10.2 + 5.11.4 test org.assertj assertj-core - 3.25.3 + 3.27.7 test @@ -106,7 +106,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.6 + 12.0.16 @@ -117,52 +117,52 @@ org.apache.maven.plugins maven-compiler-plugin - 3.12.1 + 3.15.0 org.apache.maven.plugins maven-project-info-reports-plugin - 3.5.0 + 3.8.0 org.apache.maven.plugins maven-clean-plugin - 3.3.2 + 3.5.0 org.apache.maven.plugins maven-deploy-plugin - 3.1.1 + 3.1.4 org.apache.maven.plugins maven-install-plugin - 3.1.1 + 3.1.3 org.apache.maven.plugins maven-jar-plugin - 3.3.0 + 3.4.2 org.apache.maven.plugins maven-resources-plugin - 3.3.1 + 3.5.0 org.apache.maven.plugins maven-site-plugin - 3.12.1 + 3.21.0 org.apache.maven.plugins maven-surefire-plugin - 3.2.5 + 3.5.5 org.apache.maven.plugins maven-release-plugin - 3.0.1 + 3.3.1 true false @@ -174,7 +174,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.1.0 + 3.2.8 sign-artifacts @@ -188,7 +188,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.13 + 1.7.0 true ossrh @@ -202,7 +202,7 @@ org.apache.maven.plugins maven-failsafe-plugin - 3.2.5 + 3.5.5 @@ -215,7 +215,7 @@ org.apache.maven.plugins maven-source-plugin - 3.3.0 + 3.3.1 attach-sources @@ -253,7 +253,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.6.3 + 3.11.2 ${delombok.output} none @@ -270,7 +270,7 @@ org.apache.maven.plugins maven-enforcer-plugin - 3.4.1 + 3.5.0 enforce-maven @@ -290,7 +290,7 @@ org.jacoco jacoco-maven-plugin - 0.8.11 + 0.8.14 prepare-agent @@ -334,7 +334,7 @@ org.apache.maven.plugins maven-checkstyle-plugin - 3.3.1 + 3.6.0 warning checkstyle.xml @@ -354,12 +354,12 @@ com.github.spotbugs spotbugs-maven-plugin - 4.8.3.1 + 4.9.8.2 org.owasp dependency-check-maven - 9.0.9 + 12.2.0 true diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index 59d85e5b..0c31328f 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.2-SNAPSHOT + 3.4.1-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-jakarta - 3.2.2-SNAPSHOT + 3.4.1-SNAPSHOT jar Matomo Java Tracker Servlet Jakarta @@ -29,7 +29,7 @@ jakarta.servlet jakarta.servlet-api - 6.0.0 + 6.1.0 provided @@ -53,7 +53,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.6 + 12.0.16 test diff --git a/servlet-jakarta/src/main/java/org/matomo/java/tracking/servlet/MatomoTrackerFilter.java b/servlet-jakarta/src/main/java/org/matomo/java/tracking/servlet/MatomoTrackerFilter.java index 664ce209..11ca4494 100644 --- a/servlet-jakarta/src/main/java/org/matomo/java/tracking/servlet/MatomoTrackerFilter.java +++ b/servlet-jakarta/src/main/java/org/matomo/java/tracking/servlet/MatomoTrackerFilter.java @@ -30,4 +30,15 @@ protected void doFilter(HttpServletRequest req, HttpServletResponse res, FilterC tracker.sendRequestAsync(matomoRequest); super.doFilter(req, res, chain); } + + @Override + public void destroy() { + if (tracker != null) { + try { + tracker.close(); + } catch (Exception e) { + throw new RuntimeException("Could not close tracker", e); + } + } + } } diff --git a/servlet-jakarta/src/test/java/org/matomo/java/tracking/TestSender.java b/servlet-jakarta/src/test/java/org/matomo/java/tracking/TestSender.java index d6c03833..14f8cde6 100644 --- a/servlet-jakarta/src/test/java/org/matomo/java/tracking/TestSender.java +++ b/servlet-jakarta/src/test/java/org/matomo/java/tracking/TestSender.java @@ -46,9 +46,13 @@ public void sendBulk( @NonNull @Override public CompletableFuture sendBulkAsync( - @NonNull Iterable requests, @Nullable String overrideAuthToken + @NonNull Collection requests, @Nullable String overrideAuthToken ) { throw new UnsupportedOperationException(); } + @Override + public void close() { + // Do nothing + } } diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index 4fd8bf9d..7e76f0a5 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.2-SNAPSHOT + 3.4.1-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-javax - 3.2.2-SNAPSHOT + 3.4.1-SNAPSHOT jar Matomo Java Tracker Servlet Javax @@ -53,7 +53,7 @@ org.eclipse.jetty jetty-servlet - 10.0.20 + 10.0.24 test diff --git a/servlet-javax/src/test/java/org/matomo/java/tracking/MatomoTrackerFilterIT.java b/servlet-javax/src/test/java/org/matomo/java/tracking/MatomoTrackerFilterIT.java index 62979d9b..676524c2 100644 --- a/servlet-javax/src/test/java/org/matomo/java/tracking/MatomoTrackerFilterIT.java +++ b/servlet-javax/src/test/java/org/matomo/java/tracking/MatomoTrackerFilterIT.java @@ -58,6 +58,8 @@ void sendsAnAsyncRequestOnFilter() throws Exception { ); }); + tracker.close(); + } } \ No newline at end of file diff --git a/servlet-javax/src/test/java/org/matomo/java/tracking/TestSender.java b/servlet-javax/src/test/java/org/matomo/java/tracking/TestSender.java index d6c03833..0e1704f1 100644 --- a/servlet-javax/src/test/java/org/matomo/java/tracking/TestSender.java +++ b/servlet-javax/src/test/java/org/matomo/java/tracking/TestSender.java @@ -46,9 +46,13 @@ public void sendBulk( @NonNull @Override public CompletableFuture sendBulkAsync( - @NonNull Iterable requests, @Nullable String overrideAuthToken + @NonNull Collection requests, @Nullable String overrideAuthToken ) { throw new UnsupportedOperationException(); } + @Override + public void close() { + // do nothing + } } diff --git a/spring/pom.xml b/spring/pom.xml index 8adfadc4..c55f8c43 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.2-SNAPSHOT + 3.4.1-SNAPSHOT ../pom.xml @@ -17,7 +17,7 @@ 17 17 - 3.2.2 + 3.4.2 diff --git a/test/pom.xml b/test/pom.xml index 24aeb628..7a2eb183 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.2.2-SNAPSHOT + 3.4.1-SNAPSHOT ../pom.xml @@ -46,7 +46,7 @@ org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.6 + 12.0.16 diff --git a/test/src/main/java/org/matomo/java/tracking/test/BulkExample.java b/test/src/main/java/org/matomo/java/tracking/test/BulkExample.java index d674acf2..54f35f92 100644 --- a/test/src/main/java/org/matomo/java/tracking/test/BulkExample.java +++ b/test/src/main/java/org/matomo/java/tracking/test/BulkExample.java @@ -26,18 +26,20 @@ public static void main(String[] args) { .logFailedTracking(true) .build(); - MatomoTracker tracker = new MatomoTracker(configuration); - - VisitorId visitorId = VisitorId.fromString("customer@mail.com"); - tracker.sendBulkRequestAsync( - MatomoRequests.siteSearch("Running shoes", "Running", 120L) - .visitorId(visitorId).build(), - MatomoRequests.pageView("VelocityStride ProX Running Shoes") - .visitorId(visitorId).build(), - MatomoRequests.ecommerceOrder("QXZ-789LMP", 100.0, 124.0, 19.0, 10.0, 5.0) - .visitorId(visitorId) - .build() - ); + try (MatomoTracker tracker = new MatomoTracker(configuration)) { + VisitorId visitorId = VisitorId.fromString("customer@mail.com"); + tracker.sendBulkRequestAsync( + MatomoRequests.siteSearch("Running shoes", "Running", 120L) + .visitorId(visitorId).build(), + MatomoRequests.pageView("VelocityStride ProX Running Shoes") + .visitorId(visitorId).build(), + MatomoRequests.ecommerceOrder("QXZ-789LMP", 100.0, 124.0, 19.0, 10.0, 5.0) + .visitorId(visitorId) + .build() + ); + } catch (Exception e) { + throw new RuntimeException("Could not close tracker", e); + } } diff --git a/test/src/main/java/org/matomo/java/tracking/test/ConsumerExample.java b/test/src/main/java/org/matomo/java/tracking/test/ConsumerExample.java index 3c49b87a..6c37626a 100644 --- a/test/src/main/java/org/matomo/java/tracking/test/ConsumerExample.java +++ b/test/src/main/java/org/matomo/java/tracking/test/ConsumerExample.java @@ -27,19 +27,21 @@ public static void main(String[] args) { .logFailedTracking(true) .build(); - MatomoTracker tracker = new MatomoTracker(configuration); - - MatomoRequest request = MatomoRequests - .event("Training", "Workout completed", "Bench press", 60.0) - .visitorId(VisitorId.fromString("customer@mail.com")) - .build(); - - tracker.sendRequestAsync(request) - .thenAccept(req -> System.out.printf("Sent request %s%n", req)) - .exceptionally(throwable -> { - System.err.printf("Failed to send request: %s%n", throwable.getMessage()); - return null; - }); + try (MatomoTracker tracker = new MatomoTracker(configuration)) { + MatomoRequest request = MatomoRequests + .event("Training", "Workout completed", "Bench press", 60.0) + .visitorId(VisitorId.fromString("customer@mail.com")) + .build(); + + tracker.sendRequestAsync(request) + .thenAccept(req -> System.out.printf("Sent request %s%n", req)) + .exceptionally(throwable -> { + System.err.printf("Failed to send request: %s%n", throwable.getMessage()); + return null; + }); + } catch (Exception e) { + throw new RuntimeException("Could not close tracker", e); + } } diff --git a/test/src/main/java/org/matomo/java/tracking/test/EcommerceExample.java b/test/src/main/java/org/matomo/java/tracking/test/EcommerceExample.java index 584aa32d..3f16258a 100644 --- a/test/src/main/java/org/matomo/java/tracking/test/EcommerceExample.java +++ b/test/src/main/java/org/matomo/java/tracking/test/EcommerceExample.java @@ -28,32 +28,34 @@ public static void main(String[] args) { .logFailedTracking(true) .build(); - MatomoTracker tracker = new MatomoTracker(configuration); - - tracker.sendBulkRequestAsync(MatomoRequests - .ecommerceCartUpdate(50.0) - .ecommerceItems(EcommerceItems - .builder() - .item(EcommerceItem - .builder() - .sku("XYZ12345") - .name("Matomo - The big book about web analytics") - .category("Education & Teaching") - .price(23.1) - .quantity(2) - .build()) - .item(EcommerceItem - .builder() - .sku("B0C2WV3MRJ") - .name("Matomo for data visualization") - .category("Education & Teaching") - .price(15.0) - .quantity(1) - .build()) - .build()) - .visitorId(VisitorId.fromString("customer@mail.com")) - .build() - ); + try (MatomoTracker tracker = new MatomoTracker(configuration)) { + tracker.sendBulkRequestAsync(MatomoRequests + .ecommerceCartUpdate(50.0) + .ecommerceItems(EcommerceItems + .builder() + .item(EcommerceItem + .builder() + .sku("XYZ12345") + .name("Matomo - The big book about web analytics") + .category("Education & Teaching") + .price(23.1) + .quantity(2) + .build()) + .item(EcommerceItem + .builder() + .sku("B0C2WV3MRJ") + .name("Matomo for data visualization") + .category("Education & Teaching") + .price(15.0) + .quantity(1) + .build()) + .build()) + .visitorId(VisitorId.fromString("customer@mail.com")) + .build() + ); + } catch (Exception e) { + throw new RuntimeException("Could not close tracker", e); + } } diff --git a/test/src/main/java/org/matomo/java/tracking/test/MatomoTrackerTester.java b/test/src/main/java/org/matomo/java/tracking/test/MatomoTrackerTester.java index 8255d0ed..8532c370 100644 --- a/test/src/main/java/org/matomo/java/tracking/test/MatomoTrackerTester.java +++ b/test/src/main/java/org/matomo/java/tracking/test/MatomoTrackerTester.java @@ -25,7 +25,7 @@ import org.matomo.java.tracking.parameters.VisitorId; @Slf4j -class MatomoTrackerTester { +class MatomoTrackerTester implements AutoCloseable { private final MatomoTracker tracker; @@ -39,7 +39,7 @@ class MatomoTrackerTester { } } - public static void main(String[] args) { + public static void main(String[] args) throws Exception { TrackerConfiguration configuration = TrackerConfiguration .builder() @@ -49,12 +49,12 @@ public static void main(String[] args) { .logFailedTracking(true) .build(); - MatomoTrackerTester matomoTrackerTester = new MatomoTrackerTester(configuration); - - matomoTrackerTester.sendRequestAsync(); - matomoTrackerTester.sendBulkRequestsAsync(); - matomoTrackerTester.sendRequest(); - matomoTrackerTester.sendBulkRequests(); + try (MatomoTrackerTester matomoTrackerTester = new MatomoTrackerTester(configuration)) { + matomoTrackerTester.sendRequestAsync(); + matomoTrackerTester.sendBulkRequestsAsync(); + matomoTrackerTester.sendRequest(); + matomoTrackerTester.sendBulkRequests(); + } } @@ -187,4 +187,9 @@ private MatomoRequest randomRequest() { .debug(true) .build(); } + + @Override + public void close() throws Exception { + tracker.close(); + } } diff --git a/test/src/main/java/org/matomo/java/tracking/test/SendExample.java b/test/src/main/java/org/matomo/java/tracking/test/SendExample.java index 1fe55b3e..d43bcfc3 100644 --- a/test/src/main/java/org/matomo/java/tracking/test/SendExample.java +++ b/test/src/main/java/org/matomo/java/tracking/test/SendExample.java @@ -26,13 +26,15 @@ public static void main(String[] args) { .logFailedTracking(true) .build(); - MatomoTracker tracker = new MatomoTracker(configuration); - - tracker.sendRequestAsync(MatomoRequests - .event("Training", "Workout completed", "Bench press", 60.0) - .visitorId(VisitorId.fromString("customer@mail.com")) - .build() - ); + try (MatomoTracker tracker = new MatomoTracker(configuration)) { + tracker.sendRequestAsync(MatomoRequests + .event("Training", "Workout completed", "Bench press", 60.0) + .visitorId(VisitorId.fromString("customer@mail.com")) + .build() + ); + } catch (Exception e) { + throw new RuntimeException("Could not close tracker", e); + } } }