From 3846c868e63c0a252f324eb172fab91a1153d75f Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Tue, 2 Aug 2022 14:39:01 +0200 Subject: [PATCH 1/9] Add request builder --- .editorconfig | 9 + README.md | 105 +- pom.xml | 34 +- .../matomo/java/tracking/CustomVariable.java | 35 + .../matomo/java/tracking/CustomVariables.java | 86 + .../matomo/java/tracking/EcommerceItem.java | 31 + .../matomo/java/tracking/EcommerceItems.java | 52 + .../java/tracking/HttpClientFactory.java | 89 + .../java/tracking/InvalidUrlException.java | 8 + .../matomo/java/tracking/MatomoBoolean.java | 29 + .../org/matomo/java/tracking/MatomoDate.java | 77 + .../matomo/java/tracking/MatomoLocale.java | 35 + .../matomo/java/tracking/MatomoRequest.java | 2102 ++++++++++++++++ .../java/tracking/MatomoRequestBuilder.java | 680 ++++++ .../matomo/java/tracking/MatomoTracker.java | 271 +++ .../matomo/java/tracking/QueryParameters.java | 32 + .../piwik/java/tracking/CustomVariable.java | 49 +- .../java/tracking/CustomVariableList.java | 97 - .../piwik/java/tracking/EcommerceItem.java | 164 +- .../java/tracking/HttpClientFactory.java | 104 - .../org/piwik/java/tracking/PiwikDate.java | 57 +- .../piwik/java/tracking/PiwikJsonArray.java | 72 - .../piwik/java/tracking/PiwikJsonObject.java | 117 - .../org/piwik/java/tracking/PiwikLocale.java | 50 +- .../org/piwik/java/tracking/PiwikRequest.java | 2110 +---------------- .../org/piwik/java/tracking/PiwikTracker.java | 285 +-- .../java/tracking/CustomVariableTest.java | 6 +- .../java/tracking/CustomVariablesTest.java | 85 + .../java/tracking/EcommerceItemTest.java | 27 +- .../java/tracking/EcommerceItemsTest.java | 18 + .../tracking/MatomoRequestBuilderTest.java | 48 + .../java/tracking/PiwikDateTest.java | 9 +- .../java/tracking/PiwikLocaleTest.java | 7 +- .../java/tracking/PiwikRequestTest.java | 214 +- .../java/tracking/PiwikTrackerTest.java | 51 +- .../java/tracking/CustomVariableListTest.java | 91 - .../java/tracking/PiwikJsonArrayTest.java | 73 - .../java/tracking/PiwikJsonObjectTest.java | 76 - 38 files changed, 3991 insertions(+), 3494 deletions(-) create mode 100644 .editorconfig create mode 100644 src/main/java/org/matomo/java/tracking/CustomVariable.java create mode 100644 src/main/java/org/matomo/java/tracking/CustomVariables.java create mode 100644 src/main/java/org/matomo/java/tracking/EcommerceItem.java create mode 100644 src/main/java/org/matomo/java/tracking/EcommerceItems.java create mode 100644 src/main/java/org/matomo/java/tracking/HttpClientFactory.java create mode 100644 src/main/java/org/matomo/java/tracking/InvalidUrlException.java create mode 100644 src/main/java/org/matomo/java/tracking/MatomoBoolean.java create mode 100644 src/main/java/org/matomo/java/tracking/MatomoDate.java create mode 100644 src/main/java/org/matomo/java/tracking/MatomoLocale.java create mode 100644 src/main/java/org/matomo/java/tracking/MatomoRequest.java create mode 100644 src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java create mode 100644 src/main/java/org/matomo/java/tracking/MatomoTracker.java create mode 100644 src/main/java/org/matomo/java/tracking/QueryParameters.java delete mode 100644 src/main/java/org/piwik/java/tracking/CustomVariableList.java delete mode 100644 src/main/java/org/piwik/java/tracking/HttpClientFactory.java delete mode 100644 src/main/java/org/piwik/java/tracking/PiwikJsonArray.java delete mode 100644 src/main/java/org/piwik/java/tracking/PiwikJsonObject.java rename src/test/java/org/{piwik => matomo}/java/tracking/CustomVariableTest.java (84%) create mode 100644 src/test/java/org/matomo/java/tracking/CustomVariablesTest.java rename src/test/java/org/{piwik => matomo}/java/tracking/EcommerceItemTest.java (75%) create mode 100644 src/test/java/org/matomo/java/tracking/EcommerceItemsTest.java create mode 100644 src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java rename src/test/java/org/{piwik => matomo}/java/tracking/PiwikDateTest.java (79%) rename src/test/java/org/{piwik => matomo}/java/tracking/PiwikLocaleTest.java (83%) rename src/test/java/org/{piwik => matomo}/java/tracking/PiwikRequestTest.java (82%) rename src/test/java/org/{piwik => matomo}/java/tracking/PiwikTrackerTest.java (86%) delete mode 100644 src/test/java/org/piwik/java/tracking/CustomVariableListTest.java delete mode 100644 src/test/java/org/piwik/java/tracking/PiwikJsonArrayTest.java delete mode 100644 src/test/java/org/piwik/java/tracking/PiwikJsonObjectTest.java diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..c6c8b362 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/README.md b/README.md index 952cedda..175813f9 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ -Matomo Java Tracker -================ +# Matomo Java Tracker [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.piwik.java.tracking/matomo-java-tracker/badge.svg?style=flat)](https://maven-badges.herokuapp.com/maven-central/org.piwik.java.tracking/matomo-java-tracker) -## Code Status [![Build Status](https://travis-ci.org/matomo-org/matomo-java-tracker.svg?branch=master)](https://travis-ci.org/matomo-org/matomo-java-tracker) [![Average time to resolve an issue](https://isitmaintained.com/badge/resolution/matomo-org/matomo-java-tracker.svg)](https://isitmaintained.com/project/matomo-org/matomo-java-tracker "Average time to resolve an issue") [![Percentage of issues still open](https://isitmaintained.com/badge/open/matomo-org/matomo-java-tracker.svg)](https://isitmaintained.com/project/matomo-org/matomo-java-tracker "Percentage of issues still open") @@ -10,99 +8,144 @@ Matomo Java Tracker Official Java implementation of the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api). ## Javadoc -The Javadoc for this project is hosted as a Github page for this repo. The latest Javadoc can be found [here](https://matomo-org.github.io/matomo-java-tracker/javadoc/HEAD/index.html). Javadoc for the latest and all releases can be found [here](https://matomo-org.github.io/matomo-java-tracker/javadoc/index.html). + +The Javadoc for this project is hosted as a Github page for this repo. The latest Javadoc can be +found [here](https://matomo-org.github.io/matomo-java-tracker/javadoc/HEAD/index.html). Javadoc for the latest and all +releases can be found [here](https://matomo-org.github.io/matomo-java-tracker/javadoc/index.html). ## Using this API + ### Create a Request -Each PiwikRequest represents an action the user has taken that you want tracked by your Piwik server. Create a PiwikRequest through -```java -PiwikRequest request = new PiwikRequest(siteId, actionUrl); -``` -The following parameters are also enabled by default: +Each MatomoRequest represents an action the user has taken that you want tracked by your Matomo server. Create a +MatomoRequest through ```java -required = true; -visitorId = random 16 character hex string; -randomValue = random 20 character hex string; -apiVersion = 1; -responseAsImage = false; +MatomoRequest request=MatomoRequest.builder() + .siteId(42) + .actionUrl("https://www.mydomain.com/some/site") + .actionName("Signup") + .build(); ``` +Per default every request has the following default parameters: + +| Parameter Name | Default Value | +|------------------|--------------------------------| +| required | true | +| visitorId | random 16 character hex string | +| randomValue | random 20 character hex string | +| apiVersion | 1 | +| responseAsImage | false | + Overwrite these properties as desired. -Note that if you want to be able to track campaigns using Referrers > Campaigns, you must add the correct URL parameters to your actionUrl. For example, +Note that if you want to be able to track campaigns using Referrers > Campaigns, you must add the correct +URL parameters to your actionUrl. For example, + ```java -URL actionUrl = new URL("http://example.org/landing.html?pk_campaign=Email-Nov2011&pk_kwd=LearnMore"); +URL actionUrl=new URL("http://example.org/landing.html?pk_campaign=Email-Nov2011&pk_kwd=LearnMore"); ``` + See [Tracking Campaigns](https://matomo.org/docs/tracking-campaigns/) for more information. -All HTTP query parameters denoted on the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api) can be set using the appropriate getters and setters. See PiwikRequest.java for the mappings of the parameters to their corresponding Java getters/setters. +All HTTP query parameters denoted on +the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api) can be set using the appropriate +getters and setters. See MatomoRequest.java for the mappings of the parameters to their corresponding +Java getters/setters. Some parameters are dependent on the state of other parameters: -EcommerceEnabled must be called before the following parameters are set: EcommerceId and EcommerceRevenue. +EcommerceEnabled must be called before the following parameters are set: EcommerceId and +EcommerceRevenue. -EcommerceId and EcommerceRevenue must be set before the following parameters are set: EcommerceDiscount, EcommerceItem, EcommerceLastOrderTimestamp, EcommerceShippingCost, EcommerceSubtotal, and EcommerceTax. +EcommerceId and EcommerceRevenue must be set before the following parameters are +set: EcommerceDiscount, EcommerceItem, EcommerceLastOrderTimestamp, +EcommerceShippingCost, EcommerceSubtotal, and EcommerceTax. -AuthToken must be set before the following parameters are set: VisitorCity, VisitorCountry, VisitorIp, VisitorLatitude, VisitorLongitude, and VisitorRegion. +AuthToken must be set before the following parameters are set: VisitorCity, +VisitorCountry, VisitorIp, VisitorLatitude, VisitorLongitude, and VisitorRegion +. ### Sending Requests -Create a PiwikTracker through + +Create a MatomoTracker through + ```java -PiwikTracker tracker = new PiwikTracker(hostUrl); +MatomoTracker tracker=new MatomoTracker(hostUrl); ``` -where hostUrl is the url endpoint of the Piwik server. Usually in the format http://your-piwik-domain.tld/piwik.php. + +where hostUrl is the url endpoint of the Matomo server. Usually in the format http://your-matomo-domain.tld/matomo.php. To send a single request, call + ```java -Future response = tracker.sendRequestAsync(request); +Future response=tracker.sendRequestAsync(request); ``` -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, send a bulk request. Place your requests in an Iterable data structure and call +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, +send a bulk request. Place your requests in an Iterable data structure and call + ```java -Future response = tracker.sendBulkRequestAsync(requests); +Future response=tracker.sendBulkRequestAsync(requests); ``` -If some of the parameters that you've specified in the bulk request require AuthToken to be set, this can also be set in the bulk request through + +If some of the parameters that you've specified in the bulk request require AuthToken to be set, this can also be set in +the bulk request through + ```java -Future response = tracker.sendBulkRequestAsync(requests, authToken); +Future response=tracker.sendBulkRequestAsync(requests,authToken); ``` + ## Install + This project can be tested and built by calling + ```shell mvn package ``` + The built jars and javadoc can be found in target Test this project using + ```shell mvn test ``` -This project also supports [Pitest](http://pitest.org/) mutation testing. This report can be generated by calling +This project also supports [Pitest](http://pitest.org/) mutation testing. This report can be generated by calling + ```shell mvn org.pitest:pitest-maven:mutationCoverage ``` + and will produce an HTML report at target/pit-reports/YYYYMMDDHHMI Clean this project using + ```shell mvn clean ``` ## Contribute -Have a fantastic feature idea? Spot a bug? We would absolutely love for you to contribute to this project! Please feel free to: + +Have a fantastic feature idea? Spot a bug? We would absolutely love for you to contribute to this project! Please feel +free to: * Fork this project * Create a feature branch from the master branch * Write awesome code that does awesome things * Write awesome test to test your awesome code -* Verify that everything is working as it should by running mvn test. If everything passes, you may want to make sure that your tests are covering everything you think they are! Run mvn org.pitest:pitest-maven:mutationCoverage to find out! +* Verify that everything is working as it should by running mvn test. If everything passes, you may + want to make sure that your tests are covering everything you think they are! Run mvn org.pitest: + pitest-maven:mutationCoverage to find out! * Commit this code to your repository * Submit a pull request from your branch to our dev branch and let us know why you made the changes you did * We'll take a look at your request and work to get it integrated with the repo! ## License + This software is released under the BSD 3-Clause license. See [LICENSE](LICENSE). ## Copyright + Copyright (c) 2015 General Electric Company. All rights reserved. diff --git a/pom.xml b/pom.xml index bcdb4b66..c32db240 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker - 1.8-SNAPSHOT + 2.0-SNAPSHOT jar Matomo Java Tracker @@ -98,10 +98,10 @@ 1.9.3 - org.piwik.java.tracking* + org.matomo.java.tracking* - org.piwik.java.tracking* + org.matomo.java.tracking* @@ -152,20 +152,25 @@ - - org.apache.httpcomponents - httpclient - 4.5.13 - org.apache.httpcomponents httpasyncclient 4.1.5 - org.glassfish - javax.json - 1.1.4 + com.google.guava + guava + 31.1-jre + + + com.fasterxml.jackson.core + jackson-databind + 2.13.3 + + + org.slf4j + slf4j-api + 1.7.36 org.mockito @@ -180,9 +185,10 @@ test - javax.xml.bind - jaxb-api - 2.3.1 + org.projectlombok + lombok + 1.18.24 + provided diff --git a/src/main/java/org/matomo/java/tracking/CustomVariable.java b/src/main/java/org/matomo/java/tracking/CustomVariable.java new file mode 100644 index 00000000..2d52e4d4 --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/CustomVariable.java @@ -0,0 +1,35 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ +package org.matomo.java.tracking; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NonNull; +import lombok.ToString; +import lombok.experimental.FieldDefaults; + +/** + * A user defined custom variable. + * + * @author brettcsorba + */ +@Getter +@FieldDefaults(level = AccessLevel.PRIVATE) +@AllArgsConstructor +@ToString +@EqualsAndHashCode +public class CustomVariable { + + @NonNull + String key; + + @NonNull + String value; + +} diff --git a/src/main/java/org/matomo/java/tracking/CustomVariables.java b/src/main/java/org/matomo/java/tracking/CustomVariables.java new file mode 100644 index 00000000..0bbe94df --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/CustomVariables.java @@ -0,0 +1,86 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ +package org.matomo.java.tracking; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.EqualsAndHashCode; +import lombok.NonNull; + +import javax.annotation.Nullable; +import java.util.HashMap; +import java.util.Map; + +/** + * @author brettcsorba + */ +@EqualsAndHashCode +class CustomVariables { + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + private final Map variables = new HashMap<>(); + + void add(@NonNull CustomVariable variable) { + boolean found = false; + for (Map.Entry entry : variables.entrySet()) { + CustomVariable customVariable = entry.getValue(); + if (customVariable.getKey().equals(variable.getKey())) { + variables.put(entry.getKey(), variable); + found = true; + } + } + if (!found) { + int i = 1; + while (variables.putIfAbsent(i, variable) != null) { + i++; + } + } + } + + void add(@NonNull CustomVariable cv, int index) { + if (index <= 0) { + throw new IllegalArgumentException("Index must be greater than 0."); + } + variables.put(index, cv); + } + + @Nullable + CustomVariable get(int index) { + if (index <= 0) { + throw new IllegalArgumentException("Index must be greater than 0."); + } + return variables.get(index); + } + + @Nullable + String get(@NonNull String key) { + return variables.values().stream().filter(variable -> variable.getKey().equals(key)).findFirst().map(CustomVariable::getValue).orElse(null); + } + + void remove(int index) { + variables.remove(index); + } + + void remove(@NonNull String key) { + variables.entrySet().removeIf(entry -> entry.getValue().getKey().equals(key)); + } + + boolean isEmpty() { + return variables.isEmpty(); + } + + @Override + public String toString() { + ObjectNode objectNode = OBJECT_MAPPER.createObjectNode(); + for (Map.Entry entry : variables.entrySet()) { + CustomVariable variable = entry.getValue(); + objectNode.putArray(entry.getKey().toString()).add(variable.getKey()).add(variable.getValue()); + } + return objectNode.toString(); + } +} diff --git a/src/main/java/org/matomo/java/tracking/EcommerceItem.java b/src/main/java/org/matomo/java/tracking/EcommerceItem.java new file mode 100644 index 00000000..f7b5bf23 --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/EcommerceItem.java @@ -0,0 +1,31 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ +package org.matomo.java.tracking; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +/** + * Represents an item in an ecommerce order. + * + * @author brettcsorba + */ +@Setter +@Getter +@AllArgsConstructor +@Builder +public class EcommerceItem { + + private String sku; + private String name; + private String category; + private Double price; + private Integer quantity; + +} diff --git a/src/main/java/org/matomo/java/tracking/EcommerceItems.java b/src/main/java/org/matomo/java/tracking/EcommerceItems.java new file mode 100644 index 00000000..9f435535 --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/EcommerceItems.java @@ -0,0 +1,52 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ +package org.matomo.java.tracking; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import lombok.EqualsAndHashCode; +import lombok.NonNull; + +import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.List; + +@EqualsAndHashCode +class EcommerceItems { + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + private final List ecommerceItems = new ArrayList<>(); + + public void add(@NonNull EcommerceItem ecommerceItem) { + ecommerceItems.add(ecommerceItem); + } + + @Nonnull + public EcommerceItem get(int index) { + return ecommerceItems.get(index); + } + + public EcommerceItem set(int index, EcommerceItem element) { + return ecommerceItems.set(index, element); + } + + @Override + public String toString() { + ArrayNode arrayNode = OBJECT_MAPPER.createArrayNode(); + for (EcommerceItem ecommerceItem : ecommerceItems) { + arrayNode.add(OBJECT_MAPPER.createArrayNode() + .add(ecommerceItem.getSku()) + .add(ecommerceItem.getName()) + .add(ecommerceItem.getCategory()) + .add(ecommerceItem.getPrice()) + .add(ecommerceItem.getQuantity()) + ); + } + return arrayNode.toString(); + } +} diff --git a/src/main/java/org/matomo/java/tracking/HttpClientFactory.java b/src/main/java/org/matomo/java/tracking/HttpClientFactory.java new file mode 100644 index 00000000..1ae896d8 --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/HttpClientFactory.java @@ -0,0 +1,89 @@ +package org.matomo.java.tracking; + +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import org.apache.http.HttpHost; +import org.apache.http.client.HttpClient; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.conn.DefaultProxyRoutePlanner; +import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; +import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; + +import javax.annotation.Nullable; +import java.util.HashMap; +import java.util.Map; + +/** + * Internal factory for providing instances of HTTP clients. + * Especially {@linkAsyncHttpClient} instances are intended to be global resources that share the same lifecycle as the application. + * For details see Apache documentation. + * + * @author norbertroamsys + */ +final class HttpClientFactory { + + private HttpClientFactory() { + // utility + } + + /** + * Internal key class for caching {@link CloseableHttpAsyncClient} instances. + */ + @EqualsAndHashCode + @AllArgsConstructor + private static final class KeyEntry { + + private final String proxyHost; + private final int proxyPort; + private final int timeout; + + } + + private static final Map ASYNC_INSTANCES = new HashMap<>(); + + /** + * Factory for getting a synchronous client by proxy and timeout configuration. + * The clients will be created on each call. + * + * @param proxyHost the proxy host + * @param proxyPort the proxy port + * @param timeout the timeout + * @return the created client + */ + public static HttpClient getInstanceFor(final String proxyHost, final int proxyPort, final int timeout) { + return HttpClientBuilder.create().setRoutePlanner(createRoutePlanner(proxyHost, proxyPort)).setDefaultRequestConfig(createRequestConfig(timeout)).build(); + } + + /** + * Factory for getting a asynchronous client by proxy and timeout configuration. + * The clients will be created and cached as a singleton instance. + * + * @param proxyHost the proxy host + * @param proxyPort the proxy port + * @param timeout the timeout + * @return the created client + */ + public static synchronized CloseableHttpAsyncClient getAsyncInstanceFor(final String proxyHost, final int proxyPort, final int timeout) { + return ASYNC_INSTANCES.computeIfAbsent(new KeyEntry(proxyHost, proxyPort, timeout), key -> + HttpAsyncClientBuilder.create().setRoutePlanner(createRoutePlanner(key.proxyHost, key.proxyPort)).setDefaultRequestConfig(createRequestConfig(key.timeout)).build()); + } + + @Nullable + private static DefaultProxyRoutePlanner createRoutePlanner(final String proxyHost, final int proxyPort) { + if (proxyHost != null && proxyPort != 0) { + final HttpHost proxy = new HttpHost(proxyHost, proxyPort); + return new DefaultProxyRoutePlanner(proxy); + } + return null; + } + + private static RequestConfig createRequestConfig(final int timeout) { + final RequestConfig.Builder config = RequestConfig.custom() + .setConnectTimeout(timeout) + .setConnectionRequestTimeout(timeout) + .setSocketTimeout(timeout); + return config.build(); + } + +} diff --git a/src/main/java/org/matomo/java/tracking/InvalidUrlException.java b/src/main/java/org/matomo/java/tracking/InvalidUrlException.java new file mode 100644 index 00000000..01d4fbcb --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/InvalidUrlException.java @@ -0,0 +1,8 @@ +package org.matomo.java.tracking; + +public class InvalidUrlException extends RuntimeException { + + public InvalidUrlException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/org/matomo/java/tracking/MatomoBoolean.java b/src/main/java/org/matomo/java/tracking/MatomoBoolean.java new file mode 100644 index 00000000..faa3ee92 --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/MatomoBoolean.java @@ -0,0 +1,29 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ +package org.matomo.java.tracking; + +import lombok.Value; + +/** + * Object representing a locale required by some Matomo query parameters. + * + * @author brettcsorba + */ +@Value +public class MatomoBoolean { + boolean value; + + /** + * Returns the locale's lowercase country code. + * + * @return the locale's lowercase country code + */ + @Override + public String toString() { + return value ? "1" : "0"; + } +} diff --git a/src/main/java/org/matomo/java/tracking/MatomoDate.java b/src/main/java/org/matomo/java/tracking/MatomoDate.java new file mode 100644 index 00000000..25706998 --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/MatomoDate.java @@ -0,0 +1,77 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ +package org.matomo.java.tracking; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Locale; + +/** + * A datetime object that will return the datetime in the format {@code yyyy-MM-dd hh:mm:ss}. + * + * @author brettcsorba + */ +public class MatomoDate { + private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH); + + private ZonedDateTime zonedDateTime; + + /** + * Allocates a Date object and initializes it so that it represents the time + * at which it was allocated, measured to the nearest millisecond. + */ + public MatomoDate() { + zonedDateTime = ZonedDateTime.now(ZoneOffset.UTC); + } + + /** + * Allocates a Date object and initializes it to represent the specified number + * of milliseconds since the standard base time known as "the epoch", namely + * January 1, 1970, 00:00:00 GMT. + * + * @param epochMilli the milliseconds since January 1, 1970, 00:00:00 GMT. + */ + public MatomoDate(long epochMilli) { + zonedDateTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(epochMilli), ZoneOffset.UTC); + } + + /** + * Sets the time zone of the String that will be returned by {@link #toString()}. + * Defaults to UTC. + * + * @param zone the TimeZone to set + */ + public void setTimeZone(ZoneId zone) { + zonedDateTime = zonedDateTime.withZoneSameInstant(zone); + } + + /** + * Converts this MatomoDate object to a String of the form:
+ *
+ * {@code yyyy-MM-dd hh:mm:ss}. + * + * @return a string representation of this MatomoDate + */ + @Override + public String toString() { + return DATE_TIME_FORMATTER.format(zonedDateTime); + } + + /** + * Converts this datetime to the number of milliseconds from the epoch + * of 1970-01-01T00:00:00Z. + * + * @return the number of milliseconds since the epoch of 1970-01-01T00:00:00Z + * @throws ArithmeticException if numeric overflow occurs + */ + public long getTime() { + return zonedDateTime.toInstant().toEpochMilli(); + } +} diff --git a/src/main/java/org/matomo/java/tracking/MatomoLocale.java b/src/main/java/org/matomo/java/tracking/MatomoLocale.java new file mode 100644 index 00000000..a561a17a --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/MatomoLocale.java @@ -0,0 +1,35 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ +package org.matomo.java.tracking; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +import java.util.Locale; + +/** + * Object representing a locale required by some Matomo query parameters. + * + * @author brettcsorba + */ +@Setter +@Getter +@AllArgsConstructor +public class MatomoLocale { + private Locale locale; + + /** + * Returns the locale's lowercase country code. + * + * @return the locale's lowercase country code + */ + @Override + public String toString() { + return locale.getCountry().toLowerCase(Locale.ENGLISH); + } +} diff --git a/src/main/java/org/matomo/java/tracking/MatomoRequest.java b/src/main/java/org/matomo/java/tracking/MatomoRequest.java new file mode 100644 index 00000000..2d73c0bb --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/MatomoRequest.java @@ -0,0 +1,2102 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ +package org.matomo.java.tracking; + +import com.google.common.collect.LinkedHashMultimap; +import com.google.common.collect.Multimap; +import com.google.common.io.BaseEncoding; +import lombok.NonNull; +import lombok.ToString; +import org.apache.http.client.utils.URIBuilder; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.charset.Charset; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * A class that implements the + * Matomo Tracking HTTP API. These requests can be sent using {@link MatomoTracker}. + * + * @author brettcsorba + */ +@ToString +public class MatomoRequest { + public static final int ID_LENGTH = 16; + public static final int AUTH_TOKEN_LENGTH = 32; + + private static final String ACTION_NAME = "action_name"; + private static final String ACTION_TIME = "gt_ms"; + private static final String ACTION_URL = "url"; + private static final String API_VERSION = "apiv"; + private static final String AUTH_TOKEN = "token_auth"; + private static final String CAMPAIGN_KEYWORD = "_rck"; + private static final String CAMPAIGN_NAME = "_rcn"; + private static final String CHARACTER_SET = "cs"; + private static final String CONTENT_INTERACTION = "c_i"; + private static final String CONTENT_NAME = "c_n"; + private static final String CONTENT_PIECE = "c_p"; + private static final String CONTENT_TARGET = "c_t"; + private static final String CURRENT_HOUR = "h"; + private static final String CURRENT_MINUTE = "m"; + private static final String CURRENT_SECOND = "s"; + private static final String DEVICE_RESOLUTION = "res"; + private static final String DOWNLOAD_URL = "download"; + private static final String ECOMMERCE_DISCOUNT = "ec_dt"; + private static final String ECOMMERCE_ID = "ec_id"; + private static final String ECOMMERCE_ITEMS = "ec_items"; + private static final String ECOMMERCE_LAST_ORDER_TIMESTAMP = "_ects"; + private static final String ECOMMERCE_REVENUE = "revenue"; + private static final String ECOMMERCE_SHIPPING_COST = "ec_sh"; + private static final String ECOMMERCE_SUBTOTAL = "ec_st"; + private static final String ECOMMERCE_TAX = "ec_tx"; + private static final String EVENT_ACTION = "e_a"; + private static final String EVENT_CATEGORY = "e_c"; + private static final String EVENT_NAME = "e_n"; + private static final String EVENT_VALUE = "e_v"; + private static final String HEADER_ACCEPT_LANGUAGE = "lang"; + private static final String GOAL_ID = "idgoal"; + private static final String GOAL_REVENUE = "revenue"; + private static final String HEADER_USER_AGENT = "ua"; + private static final String NEW_VISIT = "new_visit"; + private static final String OUTLINK_URL = "link"; + private static final String PAGE_CUSTOM_VARIABLE = "cvar"; + private static final String PLUGIN_DIRECTOR = "dir"; + private static final String PLUGIN_FLASH = "fla"; + private static final String PLUGIN_GEARS = "gears"; + private static final String PLUGIN_JAVA = "java"; + private static final String PLUGIN_PDF = "pdf"; + private static final String PLUGIN_QUICKTIME = "qt"; + private static final String PLUGIN_REAL_PLAYER = "realp"; + private static final String PLUGIN_SILVERLIGHT = "ag"; + private static final String PLUGIN_WINDOWS_MEDIA = "wma"; + private static final String RANDOM_VALUE = "rand"; + private static final String REFERRER_URL = "urlref"; + private static final String REQUEST_DATETIME = "cdt"; + private static final String REQUIRED = "rec"; + private static final String RESPONSE_AS_IMAGE = "send_image"; + private static final String SEARCH_CATEGORY = "search_cat"; + private static final String SEARCH_QUERY = "search"; + private static final String SEARCH_RESULTS_COUNT = "search_count"; + private static final String SITE_ID = "idsite"; + private static final String TRACK_BOT_REQUESTS = "bots"; + private static final String VISIT_CUSTOM_VARIABLE = "_cvar"; + private static final String USER_ID = "uid"; + private static final String VISITOR_CITY = "city"; + private static final String VISITOR_COUNTRY = "country"; + private static final String VISITOR_CUSTOM_ID = "cid"; + private static final String VISITOR_FIRST_VISIT_TIMESTAMP = "_idts"; + private static final String VISITOR_ID = "_id"; + private static final String VISITOR_IP = "cip"; + private static final String VISITOR_LATITUDE = "lat"; + private static final String VISITOR_LONGITUDE = "long"; + private static final String VISITOR_PREVIOUS_VISIT_TIMESTAMP = "_viewts"; + private static final String VISITOR_REGION = "region"; + private static final String VISITOR_VISIT_COUNT = "_idvc"; + + private static final int RANDOM_VALUE_LENGTH = 20; + private static final long REQUEST_DATETIME_AUTH_LIMIT = 14400000L; + private static final Pattern VISITOR_ID_PATTERN = Pattern.compile("[0-9A-Fa-f]+"); + + private final Multimap parameters = LinkedHashMultimap.create(8, 1); + + private final Set customTrackingParameterNames = new HashSet<>(2); + + /** + * Create a new request from the id of the site being tracked and the full + * url for the current action. This constructor also sets: + *
+   * {@code
+   * Required = true
+   * Visior Id = random 16 character hex string
+   * Random Value = random 20 character hex string
+   * API version = 1
+   * Response as Image = false
+   * }
+   * 
+ * Overwrite these values yourself as desired. + * + * @param siteId the id of the website we're tracking a visit/action for + * @param actionUrl the full URL for the current action + */ + public MatomoRequest(int siteId, URL actionUrl) { + setParameter(SITE_ID, siteId); + setBooleanParameter(REQUIRED, true); + setParameter(ACTION_URL, actionUrl); + setParameter(VISITOR_ID, getRandomHexString(ID_LENGTH)); + setParameter(RANDOM_VALUE, getRandomHexString(RANDOM_VALUE_LENGTH)); + setParameter(API_VERSION, "1"); + setBooleanParameter(RESPONSE_AS_IMAGE, false); + } + + public static MatomoRequestBuilder builder() { + return new MatomoRequestBuilder(); + } + + /** + * Get the title of the action being tracked + * + * @return the title of the action being tracked + */ + @Nullable + public String getActionName() { + return castOrNull(ACTION_NAME); + } + + /** + * Set the title of the action being tracked. It is possible to + * use slashes / + * to set one or several categories for this action. + * For example, Help / Feedback + * will create the Action Feedback in the category Help. + * + * @param actionName the title of the action to set. A null value will remove this parameter + */ + public void setActionName(String actionName) { + setParameter(ACTION_NAME, actionName); + } + + /** + * Get the amount of time it took the server to generate this action, in milliseconds. + * + * @return the amount of time + */ + @Nullable + public Long getActionTime() { + return castOrNull(ACTION_TIME); + } + + /** + * Set the amount of time it took the server to generate this action, in milliseconds. + * This value is used to process the + * Page speed report + * Avg. generation time column in the Page URL and Page Title reports, + * as well as a site wide running average of the speed of your server. + * + * @param actionTime the amount of time to set. A null value will remove this parameter + */ + public void setActionTime(Long actionTime) { + setParameter(ACTION_TIME, actionTime); + } + + /** + * Get the full URL for the current action. + * + * @return the full URL + */ + @Nullable + public URL getActionUrl() { + return castOrNull(ACTION_URL); + } + + /** + * Get the full URL for the current action. + * + * @return the full URL + */ + @Nullable + public String getActionUrlAsString() { + return getUrlParameter(ACTION_URL); + } + + + /** + * Set the full URL for the current action. + * + * @param actionUrl the full URL to set. A null value will remove this parameter + */ + public void setActionUrl(URL actionUrl) { + setParameter(ACTION_URL, actionUrl); + } + + /** + * Set the full URL for the current action. + * + * @param actionUrl the full URL to set. A null value will remove this parameter + */ + public void setActionUrlWithString(String actionUrl) { + setUrlParameter(ACTION_URL, actionUrl); + } + + /** + * Get the api version + * + * @return the api version + */ + @Nullable + public String getApiVersion() { + return castOrNull(API_VERSION); + } + + /** + * Set the api version to use (currently always set to 1) + * + * @param apiVersion the api version to set. A null value will remove this parameter + */ + public void setApiVersion(String apiVersion) { + setParameter(API_VERSION, apiVersion); + } + + /** + * Get the authorization key. + * + * @return the authorization key + */ + @Nullable + public String getAuthToken() { + return castOrNull(AUTH_TOKEN); + } + + /** + * Set the {@value #AUTH_TOKEN_LENGTH} character authorization key used to authenticate the API request. + * + * @param authToken the authorization key to set. A null value will remove this parameter + */ + public void setAuthToken(String authToken) { + if (authToken != null && authToken.length() != AUTH_TOKEN_LENGTH) { + throw new IllegalArgumentException(authToken + " is not " + AUTH_TOKEN_LENGTH + " characters long."); + } + setParameter(AUTH_TOKEN, authToken); + } + + /** + * Verifies that AuthToken has been set for this request. Will throw an + * {@link IllegalStateException} if not. + */ + public void verifyAuthTokenSet() { + if (getAuthToken() == null) { + throw new IllegalStateException("AuthToken must be set before this value can be set."); + } + } + + /** + * Get the campaign keyword + * + * @return the campaign keyword + */ + @Nullable + public String getCampaignKeyword() { + return castOrNull(CAMPAIGN_KEYWORD); + } + + /** + * Set the Campaign Keyword (see + * Tracking Campaigns). + * Used to populate the Referrers > Campaigns report (clicking on a + * campaign loads all keywords for this campaign). Note: this parameter + * will only be used for the first pageview of a visit. + * + * @param campaignKeyword the campaign keyword to set. A null value will remove this parameter + */ + public void setCampaignKeyword(String campaignKeyword) { + setParameter(CAMPAIGN_KEYWORD, campaignKeyword); + } + + /** + * Get the campaign name + * + * @return the campaign name + */ + @Nullable + public String getCampaignName() { + return castOrNull(CAMPAIGN_NAME); + } + + /** + * Set the Campaign Name (see + * Tracking Campaigns). + * Used to populate the Referrers > Campaigns report. Note: this parameter + * will only be used for the first pageview of a visit. + * + * @param campaignName the campaign name to set. A null value will remove this parameter + */ + public void setCampaignName(String campaignName) { + setParameter(CAMPAIGN_NAME, campaignName); + } + + /** + * Get the charset of the page being tracked + * + * @return the charset + */ + @Nullable + public Charset getCharacterSet() { + return castOrNull(CHARACTER_SET); + } + + /** + * The charset of the page being tracked. Specify the charset if the data + * you send to Matomo is encoded in a different character set than the default + * utf-8. + * + * @param characterSet the charset to set. A null value will remove this parameter + */ + public void setCharacterSet(Charset characterSet) { + setParameter(CHARACTER_SET, characterSet); + } + + /** + * Get the name of the interaction with the content + * + * @return the name of the interaction + */ + @Nullable + public String getContentInteraction() { + return castOrNull(CONTENT_INTERACTION); + } + + /** + * Set the name of the interaction with the content. For instance a 'click'. + * + * @param contentInteraction the name of the interaction to set. A null value will remove this parameter + */ + public void setContentInteraction(String contentInteraction) { + setParameter(CONTENT_INTERACTION, contentInteraction); + } + + /** + * Get the name of the content + * + * @return the name + */ + @Nullable + public String getContentName() { + return castOrNull(CONTENT_NAME); + } + + /** + * Set the name of the content. For instance 'Ad Foo Bar'. + * + * @param contentName the name to set. A null value will remove this parameter + */ + public void setContentName(String contentName) { + setParameter(CONTENT_NAME, contentName); + } + + /** + * Get the content piece. + * + * @return the content piece. + */ + @Nullable + public String getContentPiece() { + return castOrNull(CONTENT_PIECE); + } + + /** + * Set the actual content piece. For instance the path to an image, video, audio, any text. + * + * @param contentPiece the content piece to set. A null value will remove this parameter + */ + public void setContentPiece(String contentPiece) { + setParameter(CONTENT_PIECE, contentPiece); + } + + /** + * Get the content target + * + * @return the target + */ + @Nullable + public URL getContentTarget() { + return castOrNull(CONTENT_TARGET); + } + + /** + * Get the content target + * + * @return the target + */ + @Nullable + public String getContentTargetAsString() { + return getUrlParameter(CONTENT_TARGET); + } + + /** + * Set the target of the content. For instance the URL of a landing page. + * + * @param contentTarget the target to set. A null value will remove this parameter + */ + public void setContentTarget(URL contentTarget) { + setParameter(CONTENT_TARGET, contentTarget); + } + + /** + * Set the target of the content. For instance the URL of a landing page. + * + * @param contentTarget the target to set. A null value will remove this parameter + */ + public void setContentTargetWithString(String contentTarget) { + setUrlParameter(CONTENT_TARGET, contentTarget); + } + + /** + * Get the current hour. + * + * @return the current hour + */ + @Nullable + public Integer getCurrentHour() { + return castOrNull(CURRENT_HOUR); + } + + /** + * Set the current hour (local time). + * + * @param currentHour the hour to set. A null value will remove this parameter + */ + public void setCurrentHour(Integer currentHour) { + setParameter(CURRENT_HOUR, currentHour); + } + + /** + * Get the current minute. + * + * @return the current minute + */ + @Nullable + public Integer getCurrentMinute() { + return castOrNull(CURRENT_MINUTE); + } + + /** + * Set the current minute (local time). + * + * @param currentMinute the minute to set. A null value will remove this parameter + */ + public void setCurrentMinute(Integer currentMinute) { + setParameter(CURRENT_MINUTE, currentMinute); + } + + /** + * Get the current second + * + * @return the current second + */ + @Nullable + public Integer getCurrentSecond() { + return castOrNull(CURRENT_SECOND); + } + + /** + * Set the current second (local time). + * + * @param currentSecond the second to set. A null value will remove this parameter + */ + public void setCurrentSecond(Integer currentSecond) { + setParameter(CURRENT_SECOND, currentSecond); + } + + /** + * Gets the list of objects currently stored at the specified custom tracking + * parameter. An empty list will be returned if there are no objects set at + * that key. + * + * @param key the key of the parameter whose list of objects to get. Cannot be null + * @return the list of objects currently stored at the specified key + */ + public List getCustomTrackingParameter(@NonNull String key) { + return new ArrayList<>(parameters.get(key)); + } + + /** + * Set a custom tracking parameter whose toString() value will be sent to + * the Matomo server. These parameters are stored separately from named Matomo + * parameters, meaning it is not possible to overwrite or clear named Matomo + * parameters with this method. A custom parameter that has the same name + * as a named Matomo parameter will be sent in addition to that named parameter. + * + * @param key the parameter's key. Cannot be null + * @param value the parameter's value. Removes the parameter if null + */ + public void setCustomTrackingParameter(@NonNull String key, @Nullable T value) { + customTrackingParameterNames.add(key); + setParameter(key, value); + } + + /** + * Add a custom tracking parameter to the specified key. This allows users + * to have multiple parameters with the same name and different values, + * commonly used during situations where list parameters are needed + * + * @param key the parameter's key. Cannot be null + * @param value the parameter's value. Cannot be null + */ + public void addCustomTrackingParameter(@NonNull String key, @NonNull Object value) { + customTrackingParameterNames.add(key); + addParameter(key, value); + } + + /** + * Removes all custom tracking parameters + */ + public void clearCustomTrackingParameter() { + for (String customTrackingParameterName : customTrackingParameterNames) { + setParameter(customTrackingParameterName, null); + } + } + + /** + * Get the resolution of the device + * + * @return the resolution + */ + @Nullable + public String getDeviceResolution() { + return castOrNull(DEVICE_RESOLUTION); + } + + /** + * Set the resolution of the device the visitor is using, eg 1280x1024. + * + * @param deviceResolution the resolution to set. A null value will remove this parameter + */ + public void setDeviceResolution(String deviceResolution) { + setParameter(DEVICE_RESOLUTION, deviceResolution); + } + + /** + * Get the url of a file the user had downloaded + * + * @return the url + */ + @Nullable + public URL getDownloadUrl() { + return castOrNull(DOWNLOAD_URL); + } + + /** + * Get the url of a file the user had downloaded + * + * @return the url + */ + @Nullable + public String getDownloadUrlAsString() { + return getUrlParameter(DOWNLOAD_URL); + } + + /** + * Set the url of a file the user has downloaded. Used for tracking downloads. + * We recommend to also set the url parameter to this same value. + * + * @param downloadUrl the url to set. A null value will remove this parameter + */ + public void setDownloadUrl(URL downloadUrl) { + setParameter(DOWNLOAD_URL, downloadUrl); + } + + /** + * Set the url of a file the user has downloaded. Used for tracking downloads. + * We recommend to also set the url parameter to this same value. + * + * @param downloadUrl the url to set. A null value will remove this parameter + */ + public void setDownloadUrlWithString(String downloadUrl) { + setUrlParameter(DOWNLOAD_URL, downloadUrl); + } + + /** + * Sets idgoal=0 in the request to track an ecommerce interaction: + * cart update or an ecommerce order. + */ + public void enableEcommerce() { + setGoalId(0); + } + + /** + * Verifies that Ecommerce has been enabled for the request. Will throw an + * {@link IllegalStateException} if not. + */ + public void verifyEcommerceEnabled() { + if (getGoalId() == null || getGoalId() != 0) { + throw new IllegalStateException("GoalId must be \"0\". Try calling enableEcommerce first before calling this method."); + } + } + + /** + * Verifies that Ecommerce has been enabled and that Ecommerce Id and + * Ecommerce Revenue have been set for the request. Will throw an + * {@link IllegalStateException} if not. + */ + public void verifyEcommerceState() { + verifyEcommerceEnabled(); + if (getEcommerceId() == null) { + throw new IllegalStateException("EcommerceId must be set before this value can be set."); + } + if (getEcommerceRevenue() == null) { + throw new IllegalStateException("EcommerceRevenue must be set before this value can be set."); + } + } + + /** + * Get the discount offered. + * + * @return the discount + */ + @Nullable + public Double getEcommerceDiscount() { + return castOrNull(ECOMMERCE_DISCOUNT); + } + + /** + * Set the discount offered. Ecommerce must be enabled, and EcommerceId and + * EcommerceRevenue must first be set. + * + * @param discount the discount to set. A null value will remove this parameter + */ + public void setEcommerceDiscount(Double discount) { + if (discount != null) { + verifyEcommerceState(); + } + setParameter(ECOMMERCE_DISCOUNT, discount); + } + + /** + * Get the id of this order. + * + * @return the id + */ + @Nullable + public String getEcommerceId() { + return castOrNull(ECOMMERCE_ID); + } + + /** + * Set the unique string identifier for the ecommerce order (required when + * tracking an ecommerce order). Ecommerce must be enabled. + * + * @param id the id to set. A null value will remove this parameter + */ + public void setEcommerceId(String id) { + if (id != null) { + verifyEcommerceEnabled(); + } + setParameter(ECOMMERCE_ID, id); + } + + /** + * Get the {@link EcommerceItem} at the specified index + * + * @param index the index of the {@link EcommerceItem} to return + * @return the {@link EcommerceItem} at the specified index + */ + @Nullable + public EcommerceItem getEcommerceItem(int index) { + EcommerceItems ecommerceItems = castOrNull(ECOMMERCE_ITEMS); + if (ecommerceItems == null) { + return null; + } + return ecommerceItems.get(index); + } + + /** + * Add an {@link EcommerceItem} to this order. Ecommerce must be enabled, + * and EcommerceId and EcommerceRevenue must first be set. + * + * @param item the {@link EcommerceItem} to add. Cannot be null + */ + public void addEcommerceItem(@NonNull EcommerceItem item) { + verifyEcommerceState(); + EcommerceItems ecommerceItems = castOrNull(ECOMMERCE_ITEMS); + if (ecommerceItems == null) { + ecommerceItems = new EcommerceItems(); + setParameter(ECOMMERCE_ITEMS, ecommerceItems); + } + ecommerceItems.add(item); + } + + /** + * Clears all {@link EcommerceItem} from this order. + */ + public void clearEcommerceItems() { + setParameter(ECOMMERCE_ITEMS, null); + } + + /** + * Get the timestamp of the customer's last ecommerce order + * + * @return the timestamp + */ + @Nullable + public Long getEcommerceLastOrderTimestamp() { + return castOrNull(ECOMMERCE_LAST_ORDER_TIMESTAMP); + } + + /** + * Set the UNUX timestamp of this customer's last ecommerce order. This value + * is used to process the "Days since last order" report. Ecommerce must be + * enabled, and EcommerceId and EcommerceRevenue must first be set. + * + * @param timestamp the timestamp to set. A null value will remove this parameter + */ + public void setEcommerceLastOrderTimestamp(Long timestamp) { + if (timestamp != null) { + verifyEcommerceState(); + } + setParameter(ECOMMERCE_LAST_ORDER_TIMESTAMP, timestamp); + } + + /** + * Get the grand total of the ecommerce order. + * + * @return the grand total + */ + @Nullable + public Double getEcommerceRevenue() { + return castOrNull(ECOMMERCE_REVENUE); + } + + /** + * Set the grand total of the ecommerce order (required when tracking an + * ecommerce order). Ecommerce must be enabled. + * + * @param revenue the grand total to set. A null value will remove this parameter + */ + public void setEcommerceRevenue(Double revenue) { + if (revenue != null) { + verifyEcommerceEnabled(); + } + setParameter(ECOMMERCE_REVENUE, revenue); + } + + /** + * Get the shipping cost of the ecommerce order. + * + * @return the shipping cost + */ + @Nullable + public Double getEcommerceShippingCost() { + return castOrNull(ECOMMERCE_SHIPPING_COST); + } + + /** + * Set the shipping cost of the ecommerce order. Ecommerce must be enabled, + * and EcommerceId and EcommerceRevenue must first be set. + * + * @param shippingCost the shipping cost to set. A null value will remove this parameter + */ + public void setEcommerceShippingCost(Double shippingCost) { + if (shippingCost != null) { + verifyEcommerceState(); + } + setParameter(ECOMMERCE_SHIPPING_COST, shippingCost); + } + + /** + * Get the subtotal of the ecommerce order; excludes shipping. + * + * @return the subtotal + */ + @Nullable + public Double getEcommerceSubtotal() { + return castOrNull(ECOMMERCE_SUBTOTAL); + } + + /** + * Set the subtotal of the ecommerce order; excludes shipping. Ecommerce + * must be enabled and EcommerceId and EcommerceRevenue must first be set. + * + * @param subtotal the subtotal to set. A null value will remove this parameter + */ + public void setEcommerceSubtotal(Double subtotal) { + if (subtotal != null) { + verifyEcommerceState(); + } + setParameter(ECOMMERCE_SUBTOTAL, subtotal); + } + + /** + * Get the tax amount of the ecommerce order. + * + * @return the tax amount + */ + @Nullable + public Double getEcommerceTax() { + return castOrNull(ECOMMERCE_TAX); + } + + /** + * Set the tax amount of the ecommerce order. Ecommerce must be enabled, and + * EcommerceId and EcommerceRevenue must first be set. + * + * @param tax the tax amount to set. A null value will remove this parameter + */ + public void setEcommerceTax(Double tax) { + if (tax != null) { + verifyEcommerceState(); + } + setParameter(ECOMMERCE_TAX, tax); + } + + /** + * Get the event action. + * + * @return the event action + */ + @Nullable + public String getEventAction() { + return castOrNull(EVENT_ACTION); + } + + /** + * Set the event action. Must not be empty. (eg. Play, Pause, Duration, + * Add Playlist, Downloaded, Clicked...). + * + * @param eventAction the event action to set. A null value will remove this parameter + */ + public void setEventAction(String eventAction) { + setNonEmptyStringParameter(EVENT_ACTION, eventAction); + } + + /** + * Get the event category. + * + * @return the event category + */ + @Nullable + public String getEventCategory() { + return castOrNull(EVENT_CATEGORY); + } + + /** + * Set the event category. Must not be empty. (eg. Videos, Music, Games...). + * + * @param eventCategory the event category to set. A null value will remove this parameter + */ + public void setEventCategory(String eventCategory) { + setNonEmptyStringParameter(EVENT_CATEGORY, eventCategory); + } + + /** + * Get the event name. + * + * @return the event name + */ + @Nullable + public String getEventName() { + return castOrNull(EVENT_NAME); + } + + /** + * Set the event name. (eg. a Movie name, or Song name, or File name...). + * + * @param eventName the event name to set. A null value will remove this parameter + */ + public void setEventName(String eventName) { + setParameter(EVENT_NAME, eventName); + } + + /** + * Get the event value. + * + * @return the event value + */ + @Nullable + public Number getEventValue() { + return castOrNull(EVENT_VALUE); + } + + /** + * Set the event value. Must be a float or integer value (numeric), not a string. + * + * @param eventValue the event value to set. A null value will remove this parameter + */ + public void setEventValue(Number eventValue) { + setParameter(EVENT_VALUE, eventValue); + } + + /** + * Get the goal id + * + * @return the goal id + */ + @Nullable + public Integer getGoalId() { + return castOrNull(GOAL_ID); + } + + /** + * Set the goal id. If specified, the tracking request will trigger a + * conversion for the goal of the website being tracked with this id. + * + * @param goalId the goal id to set. A null value will remove this parameter + */ + public void setGoalId(Integer goalId) { + setParameter(GOAL_ID, goalId); + } + + /** + * Get the goal revenue. + * + * @return the goal revenue + */ + @Nullable + public Double getGoalRevenue() { + return castOrNull(GOAL_REVENUE); + } + + /** + * Set a monetary value that was generated as revenue by this goal conversion. + * Only used if idgoal is specified in the request. + * + * @param goalRevenue the goal revenue to set. A null value will remove this parameter + */ + public void setGoalRevenue(Double goalRevenue) { + if (goalRevenue != null && getGoalId() == null) { + throw new IllegalStateException("GoalId must be set before GoalRevenue can be set."); + } + setParameter(GOAL_REVENUE, goalRevenue); + } + + /** + * Get the Accept-Language HTTP header + * + * @return the Accept-Language HTTP header + */ + @Nullable + public String getHeaderAcceptLanguage() { + return castOrNull(HEADER_ACCEPT_LANGUAGE); + } + + /** + * Set an override value for the Accept-Language HTTP header + * field. This value is used to detect the visitor's country if + * GeoIP is not enabled. + * + * @param acceptLangage the Accept-Language HTTP header to set. A null value will remove this parameter + */ + public void setHeaderAcceptLanguage(String acceptLangage) { + setParameter(HEADER_ACCEPT_LANGUAGE, acceptLangage); + } + + /** + * Get the User-Agent HTTP header + * + * @return the User-Agent HTTP header + */ + @Nullable + public String getHeaderUserAgent() { + return castOrNull(HEADER_USER_AGENT); + } + + /** + * Set an override value for the User-Agent HTTP header field. + * The user agent is used to detect the operating system and browser used. + * + * @param userAgent the User-Agent HTTP header tos et + */ + public void setHeaderUserAgent(String userAgent) { + setParameter(HEADER_USER_AGENT, userAgent); + } + + /** + * Get if this request will force a new visit. + * + * @return true if this request will force a new visit + */ + @Nullable + public Boolean getNewVisit() { + return getBooleanParameter(NEW_VISIT); + } + + /** + * If set to true, will force a new visit to be created for this action. + * + * @param newVisit if this request will force a new visit + */ + public void setNewVisit(Boolean newVisit) { + setBooleanParameter(NEW_VISIT, newVisit); + } + + /** + * Get the outlink url + * + * @return the outlink url + */ + @Nullable + public URL getOutlinkUrl() { + return castOrNull(OUTLINK_URL); + } + + /** + * Get the outlink url + * + * @return the outlink url + */ + @Nullable + public String getOutlinkUrlAsString() { + return getUrlParameter(OUTLINK_URL); + } + + /** + * Set an external URL the user has opened. Used for tracking outlink clicks. + * We recommend to also set the url parameter to this same value. + * + * @param outlinkUrl the outlink url to set. A null value will remove this parameter + */ + public void setOutlinkUrl(URL outlinkUrl) { + setParameter(OUTLINK_URL, outlinkUrl); + } + + /** + * Set an external URL the user has opened. Used for tracking outlink clicks. + * We recommend to also set the url parameter to this same value. + * + * @param outlinkUrl the outlink url to set. A null value will remove this parameter + */ + public void setOutlinkUrlWithString(String outlinkUrl) { + setUrlParameter(OUTLINK_URL, outlinkUrl); + } + + /** + * Get the page custom variable at the specified key. + * + * @param key the key of the variable to get + * @return the variable at the specified key, null if key is not present + * @deprecated Use the {@link #getPageCustomVariable(int)} method instead. + */ + @Nullable + @Deprecated + public String getPageCustomVariable(String key) { + return getCustomVariable(PAGE_CUSTOM_VARIABLE, key); + } + + /** + * Get the page custom variable at the specified index. + * + * @param index the index of the variable to get. Must be greater than 0 + * @return the variable at the specified key, null if nothing at this index + */ + @Nullable + public CustomVariable getPageCustomVariable(int index) { + return getCustomVariable(PAGE_CUSTOM_VARIABLE, index); + } + + /** + * Set a page custom variable with the specified key and value at the first available index. + * All page custom variables with this key will be overwritten or deleted + * + * @param key the key of the variable to set + * @param value the value of the variable to set at the specified key. A null value will remove this custom variable + * @deprecated Use the {@link #setPageCustomVariable(CustomVariable, int)} method instead. + */ + @Deprecated + public void setPageCustomVariable(String key, String value) { + if (value == null) { + removeCustomVariable(PAGE_CUSTOM_VARIABLE, key); + } else { + setCustomVariable(PAGE_CUSTOM_VARIABLE, new CustomVariable(key, value), null); + } + } + + /** + * Set a page custom variable at the specified index. + * + * @param customVariable the CustomVariable to set. A null value will remove the CustomVariable at the specified index + * @param index the index of he CustomVariable to set + */ + public void setPageCustomVariable(CustomVariable customVariable, int index) { + setCustomVariable(PAGE_CUSTOM_VARIABLE, customVariable, index); + } + + /** + * Check if the visitor has the Director plugin. + * + * @return true if visitor has the Director plugin + */ + @Nullable + public Boolean getPluginDirector() { + return getBooleanParameter(PLUGIN_DIRECTOR); + } + + /** + * Set if the visitor has the Director plugin. + * + * @param director true if the visitor has the Director plugin + */ + public void setPluginDirector(Boolean director) { + setBooleanParameter(PLUGIN_DIRECTOR, director); + } + + /** + * Check if the visitor has the Flash plugin. + * + * @return true if the visitor has the Flash plugin + */ + @Nullable + public Boolean getPluginFlash() { + return getBooleanParameter(PLUGIN_FLASH); + } + + /** + * Set if the visitor has the Flash plugin. + * + * @param flash true if the visitor has the Flash plugin + */ + @Nullable + public void setPluginFlash(Boolean flash) { + setBooleanParameter(PLUGIN_FLASH, flash); + } + + /** + * Check if the visitor has the Gears plugin. + * + * @return true if the visitor has the Gears plugin + */ + @Nullable + public Boolean getPluginGears() { + return getBooleanParameter(PLUGIN_GEARS); + } + + /** + * Set if the visitor has the Gears plugin. + * + * @param gears true if the visitor has the Gears plugin + */ + public void setPluginGears(Boolean gears) { + setBooleanParameter(PLUGIN_GEARS, gears); + } + + /** + * Check if the visitor has the Java plugin. + * + * @return true if the visitor has the Java plugin + */ + @Nullable + public Boolean getPluginJava() { + return getBooleanParameter(PLUGIN_JAVA); + } + + /** + * Set if the visitor has the Java plugin. + * + * @param java true if the visitor has the Java plugin + */ + public void setPluginJava(Boolean java) { + setBooleanParameter(PLUGIN_JAVA, java); + } + + /** + * Check if the visitor has the PDF plugin. + * + * @return true if the visitor has the PDF plugin + */ + @Nullable + public Boolean getPluginPDF() { + return getBooleanParameter(PLUGIN_PDF); + } + + /** + * Set if the visitor has the PDF plugin. + * + * @param pdf true if the visitor has the PDF plugin + */ + public void setPluginPDF(Boolean pdf) { + setBooleanParameter(PLUGIN_PDF, pdf); + } + + /** + * Check if the visitor has the Quicktime plugin. + * + * @return true if the visitor has the Quicktime plugin + */ + @Nullable + public Boolean getPluginQuicktime() { + return getBooleanParameter(PLUGIN_QUICKTIME); + } + + /** + * Set if the visitor has the Quicktime plugin. + * + * @param quicktime true if the visitor has the Quicktime plugin + */ + public void setPluginQuicktime(Boolean quicktime) { + setBooleanParameter(PLUGIN_QUICKTIME, quicktime); + } + + /** + * Check if the visitor has the RealPlayer plugin. + * + * @return true if the visitor has the RealPlayer plugin + */ + @Nullable + public Boolean getPluginRealPlayer() { + return getBooleanParameter(PLUGIN_REAL_PLAYER); + } + + /** + * Set if the visitor has the RealPlayer plugin. + * + * @param realPlayer true if the visitor has the RealPlayer plugin + */ + public void setPluginRealPlayer(Boolean realPlayer) { + setBooleanParameter(PLUGIN_REAL_PLAYER, realPlayer); + } + + /** + * Check if the visitor has the Silverlight plugin. + * + * @return true if the visitor has the Silverlight plugin + */ + @Nullable + public Boolean getPluginSilverlight() { + return getBooleanParameter(PLUGIN_SILVERLIGHT); + } + + /** + * Set if the visitor has the Silverlight plugin. + * + * @param silverlight true if the visitor has the Silverlight plugin + */ + public void setPluginSilverlight(Boolean silverlight) { + setBooleanParameter(PLUGIN_SILVERLIGHT, silverlight); + } + + /** + * Check if the visitor has the Windows Media plugin. + * + * @return true if the visitor has the Windows Media plugin + */ + @Nullable + public Boolean getPluginWindowsMedia() { + return getBooleanParameter(PLUGIN_WINDOWS_MEDIA); + } + + /** + * Set if the visitor has the Windows Media plugin. + * + * @param windowsMedia true if the visitor has the Windows Media plugin + */ + public void setPluginWindowsMedia(Boolean windowsMedia) { + setBooleanParameter(PLUGIN_WINDOWS_MEDIA, windowsMedia); + } + + /** + * Get the random value for this request + * + * @return the random value + */ + @Nullable + public String getRandomValue() { + return castOrNull(RANDOM_VALUE); + } + + /** + * Set a random value that is generated before each request. Using it helps + * avoid the tracking request being cached by the browser or a proxy. + * + * @param randomValue the random value to set. A null value will remove this parameter + */ + public void setRandomValue(String randomValue) { + setParameter(RANDOM_VALUE, randomValue); + } + + /** + * Get the referrer url + * + * @return the referrer url + */ + @Nullable + public URL getReferrerUrl() { + return castOrNull(REFERRER_URL); + } + + /** + * Get the referrer url + * + * @return the referrer url + */ + @Nullable + public String getReferrerUrlAsString() { + return getUrlParameter(REFERRER_URL); + } + + /** + * Set the full HTTP Referrer URL. This value is used to determine how someone + * got to your website (ie, through a website, search engine or campaign). + * + * @param referrerUrl the referrer url to set. A null value will remove this parameter + */ + public void setReferrerUrl(URL referrerUrl) { + setParameter(REFERRER_URL, referrerUrl); + } + + /** + * Set the full HTTP Referrer URL. This value is used to determine how someone + * got to your website (ie, through a website, search engine or campaign). + * + * @param referrerUrl the referrer url to set. A null value will remove this parameter + */ + public void setReferrerUrlWithString(String referrerUrl) { + setUrlParameter(REFERRER_URL, referrerUrl); + } + + /** + * Get the datetime of the request + * + * @return the datetime of the request + */ + @Nullable + public MatomoDate getRequestDatetime() { + return castOrNull(REQUEST_DATETIME); + } + + /** + * Set the datetime of the request (normally the current time is used). + * This can be used to record visits and page views in the past. The datetime + * must be sent in UTC timezone. Note: if you record data in the past, you will + * need to force Matomo to re-process + * reports for the past dates. If you set the Request Datetime to a datetime + * older than four hours then Auth Token must be set. If you set + * Request Datetime with a datetime in the last four hours then you + * don't need to pass Auth Token. + * + * @param datetime the datetime of the request to set. A null value will remove this parameter + */ + public void setRequestDatetime(MatomoDate datetime) { + if (datetime != null && new Date().getTime() - datetime.getTime() > REQUEST_DATETIME_AUTH_LIMIT && getAuthToken() == null) { + throw new IllegalStateException("Because you are trying to set RequestDatetime for a time greater than 4 hours ago, AuthToken must be set first."); + } + setParameter(REQUEST_DATETIME, datetime); + } + + /** + * Get if this request will be tracked. + * + * @return true if request will be tracked + */ + @Nullable + public Boolean getRequired() { + return getBooleanParameter(REQUIRED); + } + + /** + * Set if this request will be tracked by the Matomo server. + * + * @param required true if request will be tracked + */ + public void setRequired(Boolean required) { + setBooleanParameter(REQUIRED, required); + } + + /** + * Get if the response will be an image. + * + * @return true if the response will be an an image + */ + @Nullable + public Boolean getResponseAsImage() { + return getBooleanParameter(RESPONSE_AS_IMAGE); + } + + /** + * Set if the response will be an image. If set to false, Matomo will respond + * with a HTTP 204 response code instead of a GIF image. This improves performance + * and can fix errors if images are not allowed to be obtained directly + * (eg Chrome Apps). Available since Matomo 2.10.0. + * + * @param responseAsImage true if the response will be an image + */ + public void setResponseAsImage(Boolean responseAsImage) { + setBooleanParameter(RESPONSE_AS_IMAGE, responseAsImage); + } + + /** + * Get the search category + * + * @return the search category + */ + @Nullable + public String getSearchCategory() { + return castOrNull(SEARCH_CATEGORY); + } + + /** + * Specify a search category with this parameter. SearchQuery must first be + * set. + * + * @param searchCategory the search category to set. A null value will remove this parameter + */ + public void setSearchCategory(String searchCategory) { + if (searchCategory != null && getSearchQuery() == null) { + throw new IllegalStateException("SearchQuery must be set before SearchCategory can be set."); + } + setParameter(SEARCH_CATEGORY, searchCategory); + } + + /** + * Get the search query. + * + * @return the search query + */ + @Nullable + public String getSearchQuery() { + return castOrNull(SEARCH_QUERY); + } + + /** + * Set the search query. When specified, the request will not be tracked as + * a normal pageview but will instead be tracked as a Site Search request. + * + * @param searchQuery the search query to set. A null value will remove this parameter + */ + public void setSearchQuery(String searchQuery) { + setParameter(SEARCH_QUERY, searchQuery); + } + + /** + * Get the search results count. + * + * @return the search results count + */ + @Nullable + public Long getSearchResultsCount() { + return castOrNull(SEARCH_RESULTS_COUNT); + } + + /** + * We recommend to set the + * search count to the number of search results displayed on the results page. + * When keywords are tracked with {@code Search Results Count=0} they will appear in + * the "No Result Search Keyword" report. SearchQuery must first be set. + * + * @param searchResultsCount the search results count to set. A null value will remove this parameter + */ + public void setSearchResultsCount(Long searchResultsCount) { + if (searchResultsCount != null && getSearchQuery() == null) { + throw new IllegalStateException("SearchQuery must be set before SearchResultsCount can be set."); + } + setParameter(SEARCH_RESULTS_COUNT, searchResultsCount); + } + + /** + * Get the id of the website we're tracking. + * + * @return the id of the website + */ + @Nullable + public Integer getSiteId() { + return castOrNull(SITE_ID); + } + + /** + * Set the ID of the website we're tracking a visit/action for. + * + * @param siteId the id of the website to set. A null value will remove this parameter + */ + public void setSiteId(Integer siteId) { + setParameter(SITE_ID, siteId); + } + + /** + * Set if bot requests should be tracked + * + * @return true if bot requests should be tracked + */ + @Nullable + public Boolean getTrackBotRequests() { + return getBooleanParameter(TRACK_BOT_REQUESTS); + } + + /** + * By default Matomo does not track bots. If you use the Tracking Java API, + * you may be interested in tracking bot requests. To enable Bot Tracking in + * Matomo, set Track Bot Requests to true. + * + * @param trackBotRequests true if bot requests should be tracked + */ + public void setTrackBotRequests(Boolean trackBotRequests) { + setBooleanParameter(TRACK_BOT_REQUESTS, trackBotRequests); + } + + /** + * Get the visit custom variable at the specified key. + * + * @param key the key of the variable to get + * @return the variable at the specified key, null if key is not present + * @deprecated Use the {@link #getVisitCustomVariable(int)} method instead. + */ + @Nullable + @Deprecated + public String getUserCustomVariable(String key) { + return getCustomVariable(VISIT_CUSTOM_VARIABLE, key); + } + + /** + * Get the visit custom variable at the specified index. + * + * @param index the index of the variable to get + * @return the variable at the specified index, null if nothing at this index + */ + @Nullable + public CustomVariable getVisitCustomVariable(int index) { + return getCustomVariable(VISIT_CUSTOM_VARIABLE, index); + } + + /** + * Set a visit custom variable with the specified key and value at the first available index. + * All visit custom variables with this key will be overwritten or deleted + * + * @param key the key of the variable to set + * @param value the value of the variable to set at the specified key. A null value will remove this parameter + * @deprecated Use the {@link #setVisitCustomVariable(CustomVariable, int)} method instead. + */ + @Deprecated + public void setUserCustomVariable(String key, String value) { + if (value == null) { + removeCustomVariable(VISIT_CUSTOM_VARIABLE, key); + } else { + setCustomVariable(VISIT_CUSTOM_VARIABLE, new CustomVariable(key, value), null); + } + } + + /** + * Set a user custom variable at the specified key. + * + * @param customVariable the CustomVariable to set. A null value will remove the custom variable at the specified index + * @param index the index to set the customVariable at. + */ + public void setVisitCustomVariable(CustomVariable customVariable, int index) { + setCustomVariable(VISIT_CUSTOM_VARIABLE, customVariable, index); + } + + /** + * Get the user id for this request. + * + * @return the user id + */ + @Nullable + public String getUserId() { + return castOrNull(USER_ID); + } + + /** + * Set the user id for this request. + * User id is any non empty unique string identifying the user (such as an email + * address or a username). To access this value, users must be logged-in in your + * system so you can fetch this user id from your system, and pass it to Matomo. + * The user id appears in the visitor log, the Visitor profile, and you can + * Segment + * reports for one or several user ids. When specified, the user id will be + * "enforced". This means that if there is no recent visit with this user id, + * a new one will be created. If a visit is found in the last 30 minutes with + * your specified user id, then the new action will be recorded to this existing visit. + * + * @param userId the user id to set. A null value will remove this parameter + */ + public void setUserId(String userId) { + setNonEmptyStringParameter(USER_ID, userId); + } + + /** + * Get the visitor's city. + * + * @return the visitor's city + */ + @Nullable + public String getVisitorCity() { + return castOrNull(VISITOR_CITY); + } + + /** + * Set an override value for the city. The name of the city the visitor is + * located in, eg, Tokyo. AuthToken must first be set. + * + * @param city the visitor's city to set. A null value will remove this parameter + */ + public void setVisitorCity(String city) { + if (city != null) { + verifyAuthTokenSet(); + } + setParameter(VISITOR_CITY, city); + } + + /** + * Get the visitor's country. + * + * @return the visitor's country + */ + @Nullable + public MatomoLocale getVisitorCountry() { + return castOrNull(VISITOR_COUNTRY); + } + + /** + * Set an override value for the country. AuthToken must first be set. + * + * @param country the visitor's country to set. A null value will remove this parameter + */ + public void setVisitorCountry(MatomoLocale country) { + if (country != null) { + verifyAuthTokenSet(); + } + setParameter(VISITOR_COUNTRY, country); + } + + /** + * Get the visitor's custom id. + * + * @return the visitor's custom id + */ + @Nullable + public String getVisitorCustomId() { + return castOrNull(VISITOR_CUSTOM_ID); + } + + /** + * Set a custom visitor ID for this request. You must set this value to exactly + * a {@value #ID_LENGTH} character hexadecimal string (containing only characters 01234567890abcdefABCDEF). + * We recommended to set the UserId rather than the VisitorCustomId. + * + * @param visitorCustomId the visitor's custom id to set. A null value will remove this parameter + */ + public void setVisitorCustomId(String visitorCustomId) { + if (visitorCustomId != null) { + if (visitorCustomId.length() != ID_LENGTH) { + throw new IllegalArgumentException(visitorCustomId + " is not " + ID_LENGTH + " characters long."); + } + // Verify visitorID is a 16 character hexadecimal string + if (!VISITOR_ID_PATTERN.matcher(visitorCustomId).matches()) { + throw new IllegalArgumentException(visitorCustomId + " is not a hexadecimal string."); + } + } + setParameter(VISITOR_CUSTOM_ID, visitorCustomId); + } + + /** + * Get the timestamp of the visitor's first visit. + * + * @return the timestamp of the visitor's first visit + */ + @Nullable + public Long getVisitorFirstVisitTimestamp() { + return castOrNull(VISITOR_FIRST_VISIT_TIMESTAMP); + } + + /** + * Set the UNIX timestamp of this visitor's first visit. This could be set + * to the date where the user first started using your software/app, or when + * he/she created an account. This parameter is used to populate the + * Goals > Days to Conversion report. + * + * @param timestamp the timestamp of the visitor's first visit to set. A null value will remove this parameter + */ + public void setVisitorFirstVisitTimestamp(Long timestamp) { + setParameter(VISITOR_FIRST_VISIT_TIMESTAMP, timestamp); + } + + /** + * Get the visitor's id. + * + * @return the visitor's id + */ + @Nullable + public String getVisitorId() { + return castOrNull(VISITOR_ID); + } + + /** + * Set the unique visitor ID, must be a {@value #ID_LENGTH} characters hexadecimal string. + * Every unique visitor must be assigned a different ID and this ID must not + * change after it is assigned. If this value is not set Matomo will still + * track visits, but the unique visitors metric might be less accurate. + * + * @param visitorId the visitor id to set. A null value will remove this parameter + */ + public void setVisitorId(String visitorId) { + if (visitorId != null) { + if (visitorId.length() != ID_LENGTH) { + throw new IllegalArgumentException(visitorId + " is not " + ID_LENGTH + " characters long."); + } + // Verify visitorID is a 16 character hexadecimal string + if (!VISITOR_ID_PATTERN.matcher(visitorId).matches()) { + throw new IllegalArgumentException(visitorId + " is not a hexadecimal string."); + } + } + setParameter(VISITOR_ID, visitorId); + } + + /** + * Get the visitor's ip. + * + * @return the visitor's ip + */ + @Nullable + public String getVisitorIp() { + return castOrNull(VISITOR_IP); + } + + /** + * Set the override value for the visitor IP (both IPv4 and IPv6 notations + * supported). AuthToken must first be set. + * + * @param visitorIp the visitor's ip to set. A null value will remove this parameter + */ + public void setVisitorIp(String visitorIp) { + if (visitorIp != null) { + verifyAuthTokenSet(); + } + setParameter(VISITOR_IP, visitorIp); + } + + /** + * Get the visitor's latitude. + * + * @return the visitor's latitude + */ + @Nullable + public Double getVisitorLatitude() { + return castOrNull(VISITOR_LATITUDE); + } + + /** + * Set an override value for the visitor's latitude, eg 22.456. AuthToken + * must first be set. + * + * @param latitude the visitor's latitude to set. A null value will remove this parameter + */ + public void setVisitorLatitude(Double latitude) { + if (latitude != null) { + verifyAuthTokenSet(); + } + setParameter(VISITOR_LATITUDE, latitude); + } + + /** + * Get the visitor's longitude. + * + * @return the visitor's longitude + */ + @Nullable + public Double getVisitorLongitude() { + return castOrNull(VISITOR_LONGITUDE); + } + + /** + * Set an override value for the visitor's longitude, eg 22.456. AuthToken + * must first be set. + * + * @param longitude the visitor's longitude to set. A null value will remove this parameter + */ + public void setVisitorLongitude(Double longitude) { + if (longitude != null) { + verifyAuthTokenSet(); + } + setParameter(VISITOR_LONGITUDE, longitude); + } + + /** + * Get the timestamp of the visitor's previous visit. + * + * @return the timestamp of the visitor's previous visit + */ + @Nullable + public Long getVisitorPreviousVisitTimestamp() { + return castOrNull(VISITOR_PREVIOUS_VISIT_TIMESTAMP); + } + + /** + * Set the UNIX timestamp of this visitor's previous visit. This parameter + * is used to populate the report + * Visitors > Engagement > Visits by days since last visit. + * + * @param timestamp the timestamp of the visitor's previous visit to set. A null value will remove this parameter + */ + public void setVisitorPreviousVisitTimestamp(Long timestamp) { + setParameter(VISITOR_PREVIOUS_VISIT_TIMESTAMP, timestamp); + } + + /** + * Get the visitor's region. + * + * @return the visitor's region + */ + @Nullable + public String getVisitorRegion() { + return castOrNull(VISITOR_REGION); + } + + /** + * Set an override value for the region. Should be set to the two letter + * region code as defined by + * MaxMind's GeoIP databases. + * See here + * for a list of them for every country (the region codes are located in the + * second column, to the left of the region name and to the right of the country + * code). + * + * @param region the visitor's region to set. A null value will remove this parameter + */ + public void setVisitorRegion(String region) { + if (region != null) { + verifyAuthTokenSet(); + } + setParameter(VISITOR_REGION, region); + } + + /** + * Get the count of visits for this visitor. + * + * @return the count of visits for this visitor + */ + @Nullable + public Integer getVisitorVisitCount() { + return castOrNull(VISITOR_VISIT_COUNT); + } + + /** + * Set the current count of visits for this visitor. To set this value correctly, + * it would be required to store the value for each visitor in your application + * (using sessions or persisting in a database). Then you would manually increment + * the counts by one on each new visit or "session", depending on how you choose + * to define a visit. This value is used to populate the report + * Visitors > Engagement > Visits by visit number. + * + * @param visitorVisitCount the count of visits for this visitor to set. A null value will remove this parameter + */ + public void setVisitorVisitCount(Integer visitorVisitCount) { + setParameter(VISITOR_VISIT_COUNT, visitorVisitCount); + } + + public Map> getParameters() { + return parameters.asMap(); + } + + /** + * Get the query string represented by this object. + * + * @return the query string represented by this object + * @deprecated Use {@link URIBuilder} in conjunction with {@link #getParameters()} and {@link QueryParameters#fromMap(Map)} ()} instead + */ + @Nonnull + @Deprecated + public String getQueryString() { + return parameters.entries().stream().map(parameter -> parameter.getKey() + '=' + parameter.getValue().toString()).collect(Collectors.joining("&")); + } + + /** + * Get the url encoded query string represented by this object. + * + * @return the url encoded query string represented by this object + * @deprecated Use {@link URIBuilder} in conjunction with {@link #getParameters()} and {@link QueryParameters#fromMap(Map)} ()} instead + */ + @Nonnull + @Deprecated + public String getUrlEncodedQueryString() { + String queryString = new URIBuilder().setParameters(QueryParameters.fromMap(getParameters())).toString(); + if (queryString.isEmpty()) { + return ""; + } + return queryString.substring(1); + } + + /** + * Get a random hexadecimal string of a specified length. + * + * @param length length of the string to produce + * @return a random string consisting only of hexadecimal characters + */ + @Nonnull + public static String getRandomHexString(int length) { + byte[] bytes = new byte[length / 2]; + new SecureRandom().nextBytes(bytes); + return BaseEncoding.base16().lowerCase().encode(bytes); + } + + /** + * Set a stored parameter. + * + * @param key the parameter's key + * @param value the parameter's value. Removes the parameter if null + */ + public void setParameter(@NonNull String key, @Nullable Object value) { + parameters.removeAll(key); + if (value != null) { + addParameter(key, value); + } + } + + /** + * Add more values to the given parameter + * + * @param key the parameter's key. Must not be null + * @param value the parameter's value. Must not be null + */ + public void addParameter(@NonNull String key, @NonNull Object value) { + parameters.put(key, value); + } + + + /** + * Get a stored parameter that and cast it if present + * + * @param key the parameter's key. Must not be null + * @return the stored parameter's value casted to the requested type or null if no value is present + */ + @Nullable + private T castOrNull(@NonNull String key) { + Collection values = parameters.get(key); + if (values.isEmpty()) { + return null; + } + return (T) values.iterator().next(); + } + + /** + * Set a stored parameter and verify it is a non-empty string. + * + * @param key the parameter's key + * @param value the parameter's value. Cannot be the empty. Removes the parameter if null + * string + */ + private void setNonEmptyStringParameter(@NonNull String key, String value) { + if (value != null && value.trim().isEmpty()) { + throw new IllegalArgumentException("Value cannot be empty."); + } + setParameter(key, value); + } + + /** + * Get a stored parameter that is a boolean. + * + * @param key the parameter's key + * @return the stored parameter's value + */ + @Nullable + private Boolean getBooleanParameter(@NonNull String key) { + MatomoBoolean matomoBoolean = castOrNull(key); + if (matomoBoolean == null) { + return null; + } + return matomoBoolean.isValue(); + } + + /** + * Set a stored parameter that is a boolean. + * + * @param key the parameter's key + * @param value the parameter's value. Removes the parameter if null + */ + private void setBooleanParameter(@NonNull String key, @Nullable Boolean value) { + if (value == null) { + setParameter(key, null); + } else { + setParameter(key, new MatomoBoolean(value)); + } + } + + /** + * Get a stored parameter that is a URL. + * + * @param key the parameter's key + * @return the stored parameter's value + */ + @Nullable + private String getUrlParameter(@NonNull String key) { + URL url = castOrNull(key); + if (url == null) { + return null; + } + return url.toString(); + } + + /** + * Set a stored parameter that is a valid URL. It will be syntactically checked + * + * @param key the parameter's key + * @param url the parameter's value. Removes the parameter if null + * @throws InvalidUrlException if the URL is invalid + */ + private void setUrlParameter(@NonNull String key, @Nullable String url) { + if (url == null) { + setParameter(key, null); + } else { + try { + setParameter(key, new URL(url)); + } catch (MalformedURLException e) { + throw new InvalidUrlException(e); + } + } + + } + + /** + * Get a value that is stored in a json object at the specified parameter. + * + * @param parameter the parameter to retrieve the json object from + * @param index the index of the value. + * @return the value at the specified index + */ + @Nullable + private CustomVariable getCustomVariable(@NonNull String parameter, int index) { + CustomVariables customVariables = castOrNull(parameter); + if (customVariables == null) { + return null; + } + return customVariables.get(index); + } + + @Nullable + private String getCustomVariable(@NonNull String parameter, @NonNull String key) { + CustomVariables customVariables = castOrNull(parameter); + if (customVariables == null) { + return null; + } + return customVariables.get(key); + } + + /** + * Store a value in a json object at the specified parameter. + * + * @param parameter the parameter to store the json object at + * @param parameter the key of the value. Cannot be null + * @param customVariable the value. Removes the parameter if null + */ + private void setCustomVariable(@NonNull String parameter, @Nullable CustomVariable customVariable, Integer index) { + + if (customVariable == null && index == null) { + throw new IllegalArgumentException("Either custom variable or index must be set"); + } + CustomVariables customVariables = castOrNull(parameter); + if (customVariables == null) { + customVariables = new CustomVariables(); + setParameter(parameter, customVariables); + } + if (customVariable == null) { + customVariables.remove(index); + if (customVariables.isEmpty()) { + setParameter(parameter, null); + } + } else if (index == null) { + customVariables.add(customVariable); + } else { + customVariables.add(customVariable, index); + } + } + + private void removeCustomVariable(@NonNull String parameter, @NonNull String key) { + CustomVariables customVariables = castOrNull(parameter); + if (customVariables != null) { + customVariables.remove(key); + if (customVariables.isEmpty()) { + setParameter(parameter, null); + } + } + } + +} diff --git a/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java b/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java new file mode 100644 index 00000000..70b60ad3 --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java @@ -0,0 +1,680 @@ +package org.matomo.java.tracking; + +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.charset.Charset; +import java.util.List; +import java.util.Map; + +public class MatomoRequestBuilder { + + private int siteId; + + private URL actionUrl; + + private String actionName; + private Long actionTime; + private String apiVersion; + private String authToken; + private String campaignKeyword; + private String campaignName; + private Charset characterSet; + private String contentInteraction; + private String contentName; + private String contentPiece; + private URL contentTarget; + private Integer currentHour; + private Integer currentMinute; + private Integer currentSecond; + private String deviceResolution; + private URL downloadUrl; + private Double ecommerceDiscount; + private String ecommerceId; + private Long ecommerceLastOrderTimestamp; + private Double ecommerceRevenue; + private Double ecommerceShippingCost; + private Double ecommerceSubtotal; + private Double ecommerceTax; + private String eventAction; + private String eventCategory; + private String eventName; + private Number eventValue; + private Integer goalId; + private Double goalRevenue; + private String headerAcceptLanguage; + private String headerUserAgent; + private Boolean newVisit; + private URL outlinkUrl; + private Boolean pluginDirector; + private Boolean pluginFlash; + private Boolean pluginGears; + private Boolean pluginJava; + private Boolean pluginPDF; + private Boolean pluginQuicktime; + private Boolean pluginRealPlayer; + private Boolean pluginSilverlight; + private Boolean pluginWindowsMedia; + private String randomValue; + private URL referrerUrl; + private MatomoDate requestDatetime; + private Boolean required; + private Boolean responseAsImage; + private String searchCategory; + private String searchQuery; + private Long searchResultsCount; + private Boolean trackBotRequests; + private String userId; + private String visitorCity; + private MatomoLocale visitorCountry; + private String visitorCustomId; + private Long visitorFirstVisitTimestamp; + private String visitorId; + private String visitorIp; + private Double visitorLatitude; + private Double visitorLongitude; + private Long visitorPreviousVisitTimestamp; + private String visitorRegion; + private Integer visitorVisitCount; + + private List visitCustomVariables; + + private List pageCustomVariables; + private Map customTrackingParameters; + + public MatomoRequestBuilder siteId(int siteId) { + this.siteId = siteId; + return this; + } + + public MatomoRequestBuilder actionUrl(URL actionUrl) { + this.actionUrl = actionUrl; + return this; + } + + public MatomoRequestBuilder actionUrl(String actionUrl) { + try { + this.actionUrl = new URL(actionUrl); + } catch (MalformedURLException e) { + throw new InvalidUrlException(e); + } + return this; + } + + public MatomoRequestBuilder actionName(String actionName) { + this.actionName = actionName; + return this; + } + + public MatomoRequestBuilder actionTime(Long actionTime) { + this.actionTime = actionTime; + return this; + } + + public MatomoRequestBuilder apiVersion(String apiVersion) { + this.apiVersion = apiVersion; + return this; + } + + public MatomoRequestBuilder authToken(String authToken) { + this.authToken = authToken; + return this; + } + + public MatomoRequestBuilder campaignKeyword(String campaignKeyword) { + this.campaignKeyword = campaignKeyword; + return this; + } + + public MatomoRequestBuilder campaignName(String campaignName) { + this.campaignName = campaignName; + return this; + } + + public MatomoRequestBuilder characterSet(Charset characterSet) { + this.characterSet = characterSet; + return this; + } + + public MatomoRequestBuilder contentInteraction(String contentInteraction) { + this.contentInteraction = contentInteraction; + return this; + } + + public MatomoRequestBuilder contentName(String contentName) { + this.contentName = contentName; + return this; + } + + public MatomoRequestBuilder contentPiece(String contentPiece) { + this.contentPiece = contentPiece; + return this; + } + + public MatomoRequestBuilder contentTarget(URL contentTarget) { + this.contentTarget = contentTarget; + return this; + } + + public MatomoRequestBuilder contentTarget(String contentTarget) { + try { + this.contentTarget = new URL(contentTarget); + } catch (MalformedURLException e) { + throw new InvalidUrlException(e); + } + return this; + } + + public MatomoRequestBuilder currentHour(Integer currentHour) { + this.currentHour = currentHour; + return this; + } + + public MatomoRequestBuilder currentMinute(Integer currentMinute) { + this.currentMinute = currentMinute; + return this; + } + + public MatomoRequestBuilder currentSecond(Integer currentSecond) { + this.currentSecond = currentSecond; + return this; + } + + public MatomoRequestBuilder deviceResolution(String deviceResolution) { + this.deviceResolution = deviceResolution; + return this; + } + + public MatomoRequestBuilder downloadUrl(URL downloadUrl) { + this.downloadUrl = downloadUrl; + return this; + } + + public MatomoRequestBuilder downloadUrl(String downloadUrl) { + try { + this.downloadUrl = new URL(downloadUrl); + } catch (MalformedURLException e) { + throw new InvalidUrlException(e); + } + return this; + } + + public MatomoRequestBuilder ecommerceDiscount(Double ecommerceDiscount) { + this.ecommerceDiscount = ecommerceDiscount; + return this; + } + + public MatomoRequestBuilder ecommerceId(String ecommerceId) { + this.ecommerceId = ecommerceId; + return this; + } + + public MatomoRequestBuilder ecommerceLastOrderTimestamp(Long ecommerceLastOrderTimestamp) { + this.ecommerceLastOrderTimestamp = ecommerceLastOrderTimestamp; + return this; + } + + public MatomoRequestBuilder ecommerceRevenue(Double ecommerceRevenue) { + this.ecommerceRevenue = ecommerceRevenue; + return this; + } + + public MatomoRequestBuilder ecommerceShippingCost(Double ecommerceShippingCost) { + this.ecommerceShippingCost = ecommerceShippingCost; + return this; + } + + public MatomoRequestBuilder ecommerceSubtotal(Double ecommerceSubtotal) { + this.ecommerceSubtotal = ecommerceSubtotal; + return this; + } + + public MatomoRequestBuilder ecommerceTax(Double ecommerceTax) { + this.ecommerceTax = ecommerceTax; + return this; + } + + public MatomoRequestBuilder eventAction(String eventAction) { + this.eventAction = eventAction; + return this; + } + + public MatomoRequestBuilder eventCategory(String eventCategory) { + this.eventCategory = eventCategory; + return this; + } + + public MatomoRequestBuilder eventName(String eventName) { + this.eventName = eventName; + return this; + } + + public MatomoRequestBuilder eventValue(Number eventValue) { + this.eventValue = eventValue; + return this; + } + + public MatomoRequestBuilder goalId(Integer goalId) { + this.goalId = goalId; + return this; + } + + public MatomoRequestBuilder goalRevenue(Double goalRevenue) { + this.goalRevenue = goalRevenue; + return this; + } + + public MatomoRequestBuilder headerAcceptLanguage(String headerAcceptLanguage) { + this.headerAcceptLanguage = headerAcceptLanguage; + return this; + } + + public MatomoRequestBuilder headerUserAgent(String headerUserAgent) { + this.headerUserAgent = headerUserAgent; + return this; + } + + public MatomoRequestBuilder newVisit(Boolean newVisit) { + this.newVisit = newVisit; + return this; + } + + public MatomoRequestBuilder outlinkUrl(URL outlinkUrl) { + this.outlinkUrl = outlinkUrl; + return this; + } + + public MatomoRequestBuilder outlinkUrl(String outlinkUrl) { + try { + this.outlinkUrl = new URL(outlinkUrl); + } catch (MalformedURLException e) { + throw new InvalidUrlException(e); + } + return this; + } + + public MatomoRequestBuilder pluginDirector(Boolean pluginDirector) { + this.pluginDirector = pluginDirector; + return this; + } + + public MatomoRequestBuilder pluginFlash(Boolean pluginFlash) { + this.pluginFlash = pluginFlash; + return this; + } + + public MatomoRequestBuilder pluginGears(Boolean pluginGears) { + this.pluginGears = pluginGears; + return this; + } + + public MatomoRequestBuilder pluginJava(Boolean pluginJava) { + this.pluginJava = pluginJava; + return this; + } + + public MatomoRequestBuilder pluginPDF(Boolean pluginPDF) { + this.pluginPDF = pluginPDF; + return this; + } + + public MatomoRequestBuilder pluginQuicktime(Boolean pluginQuicktime) { + this.pluginQuicktime = pluginQuicktime; + return this; + } + + public MatomoRequestBuilder pluginRealPlayer(Boolean pluginRealPlayer) { + this.pluginRealPlayer = pluginRealPlayer; + return this; + } + + public MatomoRequestBuilder pluginSilverlight(Boolean pluginSilverlight) { + this.pluginSilverlight = pluginSilverlight; + return this; + } + + public MatomoRequestBuilder pluginWindowsMedia(Boolean pluginWindowsMedia) { + this.pluginWindowsMedia = pluginWindowsMedia; + return this; + } + + public MatomoRequestBuilder randomValue(String randomValue) { + this.randomValue = randomValue; + return this; + } + + public MatomoRequestBuilder referrerUrl(URL referrerUrl) { + this.referrerUrl = referrerUrl; + return this; + } + + public MatomoRequestBuilder referrerUrl(String referrerUrl) { + try { + this.referrerUrl = new URL(referrerUrl); + } catch (MalformedURLException e) { + throw new InvalidUrlException(e); + } + return this; + } + + public MatomoRequestBuilder requestDatetime(MatomoDate requestDatetime) { + this.requestDatetime = requestDatetime; + return this; + } + + public MatomoRequestBuilder required(Boolean required) { + this.required = required; + return this; + } + + public MatomoRequestBuilder responseAsImage(Boolean responseAsImage) { + this.responseAsImage = responseAsImage; + return this; + } + + public MatomoRequestBuilder searchCategory(String searchCategory) { + this.searchCategory = searchCategory; + return this; + } + + public MatomoRequestBuilder searchQuery(String searchQuery) { + this.searchQuery = searchQuery; + return this; + } + + public MatomoRequestBuilder searchResultsCount(Long searchResultsCount) { + this.searchResultsCount = searchResultsCount; + return this; + } + + public MatomoRequestBuilder trackBotRequests(Boolean trackBotRequests) { + this.trackBotRequests = trackBotRequests; + return this; + } + + public MatomoRequestBuilder userId(String userId) { + this.userId = userId; + return this; + } + + public MatomoRequestBuilder visitorCity(String visitorCity) { + this.visitorCity = visitorCity; + return this; + } + + public MatomoRequestBuilder visitorCountry(MatomoLocale visitorCountry) { + this.visitorCountry = visitorCountry; + return this; + } + + public MatomoRequestBuilder visitorCustomId(String visitorCustomId) { + this.visitorCustomId = visitorCustomId; + return this; + } + + public MatomoRequestBuilder visitorFirstVisitTimestamp(Long visitorFirstVisitTimestamp) { + this.visitorFirstVisitTimestamp = visitorFirstVisitTimestamp; + return this; + } + + public MatomoRequestBuilder visitorId(String visitorId) { + this.visitorId = visitorId; + return this; + } + + public MatomoRequestBuilder visitorIp(String visitorIp) { + this.visitorIp = visitorIp; + return this; + } + + public MatomoRequestBuilder visitorLatitude(Double visitorLatitude) { + this.visitorLatitude = visitorLatitude; + return this; + } + + public MatomoRequestBuilder visitorLongitude(Double visitorLongitude) { + this.visitorLongitude = visitorLongitude; + return this; + } + + public MatomoRequestBuilder visitorPreviousVisitTimestamp(Long visitorPreviousVisitTimestamp) { + this.visitorPreviousVisitTimestamp = visitorPreviousVisitTimestamp; + return this; + } + + public MatomoRequestBuilder visitorRegion(String visitorRegion) { + this.visitorRegion = visitorRegion; + return this; + } + + public MatomoRequestBuilder visitorVisitCount(Integer visitorVisitCount) { + this.visitorVisitCount = visitorVisitCount; + return this; + } + + public MatomoRequestBuilder visitCustomVariables(List visitCustomVariables) { + this.visitCustomVariables = visitCustomVariables; + return this; + } + + public MatomoRequestBuilder pageCustomVariables(List pageCustomVariables) { + this.pageCustomVariables = pageCustomVariables; + return this; + } + + public MatomoRequestBuilder customTrackingParameters(Map customTrackingParameters) { + this.customTrackingParameters = customTrackingParameters; + return this; + } + + public MatomoRequest build() { + MatomoRequest matomoRequest = new MatomoRequest(siteId, actionUrl); + if (actionName != null) { + matomoRequest.setActionName(actionName); + } + if (actionTime != null) { + matomoRequest.setActionTime(actionTime); + } + if (apiVersion != null) { + matomoRequest.setApiVersion(apiVersion); + } + if (authToken != null) { + matomoRequest.setAuthToken(authToken); + } + if (campaignKeyword != null) { + matomoRequest.setCampaignKeyword(campaignKeyword); + } + if (campaignName != null) { + matomoRequest.setCampaignName(campaignName); + } + if (characterSet != null) { + matomoRequest.setCharacterSet(characterSet); + } + if (contentInteraction != null) { + matomoRequest.setContentInteraction(contentInteraction); + } + if (contentName != null) { + matomoRequest.setContentName(contentName); + } + if (contentPiece != null) { + matomoRequest.setContentPiece(contentPiece); + } + if (contentTarget != null) { + matomoRequest.setContentTarget(contentTarget); + } + if (currentHour != null) { + matomoRequest.setCurrentHour(currentHour); + } + if (currentMinute != null) { + matomoRequest.setCurrentMinute(currentMinute); + } + if (currentSecond != null) { + matomoRequest.setCurrentSecond(currentSecond); + } + if (customTrackingParameters != null) { + for (Map.Entry customTrackingParameter : customTrackingParameters.entrySet()) { + matomoRequest.addCustomTrackingParameter(customTrackingParameter.getKey(), customTrackingParameter.getValue()); + } + } + if (deviceResolution != null) { + matomoRequest.setDeviceResolution(deviceResolution); + } + if (downloadUrl != null) { + matomoRequest.setDownloadUrl(downloadUrl); + } + if (ecommerceDiscount != null) { + matomoRequest.setEcommerceDiscount(ecommerceDiscount); + } + if (ecommerceId != null) { + matomoRequest.setEcommerceId(ecommerceId); + } + if (ecommerceLastOrderTimestamp != null) { + matomoRequest.setEcommerceLastOrderTimestamp(ecommerceLastOrderTimestamp); + } + if (ecommerceRevenue != null) { + matomoRequest.setEcommerceRevenue(ecommerceRevenue); + } + if (ecommerceShippingCost != null) { + matomoRequest.setEcommerceShippingCost(ecommerceShippingCost); + } + if (ecommerceSubtotal != null) { + matomoRequest.setEcommerceSubtotal(ecommerceSubtotal); + } + if (ecommerceTax != null) { + matomoRequest.setEcommerceTax(ecommerceTax); + } + if (eventAction != null) { + matomoRequest.setEventAction(eventAction); + } + if (eventCategory != null) { + matomoRequest.setEventCategory(eventCategory); + } + if (eventName != null) { + matomoRequest.setEventName(eventName); + } + if (eventValue != null) { + matomoRequest.setEventValue(eventValue); + } + if (goalId != null) { + matomoRequest.setGoalId(goalId); + } + if (goalRevenue != null) { + matomoRequest.setGoalRevenue(goalRevenue); + } + if (headerAcceptLanguage != null) { + matomoRequest.setHeaderAcceptLanguage(headerAcceptLanguage); + } + if (headerUserAgent != null) { + matomoRequest.setHeaderUserAgent(headerUserAgent); + } + if (newVisit != null) { + matomoRequest.setNewVisit(newVisit); + } + if (outlinkUrl != null) { + matomoRequest.setOutlinkUrl(outlinkUrl); + } + if (pageCustomVariables != null) { + for (int i = 0; i < pageCustomVariables.size(); i++) { + CustomVariable pageCustomVariable = pageCustomVariables.get(i); + matomoRequest.setPageCustomVariable(pageCustomVariable, i + 1); + } + } + if (pluginDirector != null) { + matomoRequest.setPluginDirector(pluginDirector); + } + if (pluginFlash != null) { + matomoRequest.setPluginFlash(pluginFlash); + } + if (pluginGears != null) { + matomoRequest.setPluginGears(pluginGears); + } + if (pluginJava != null) { + matomoRequest.setPluginJava(pluginJava); + } + if (pluginPDF != null) { + matomoRequest.setPluginPDF(pluginPDF); + } + if (pluginQuicktime != null) { + matomoRequest.setPluginQuicktime(pluginQuicktime); + } + if (pluginRealPlayer != null) { + matomoRequest.setPluginRealPlayer(pluginRealPlayer); + } + if (pluginSilverlight != null) { + matomoRequest.setPluginSilverlight(pluginSilverlight); + } + if (pluginWindowsMedia != null) { + matomoRequest.setPluginWindowsMedia(pluginWindowsMedia); + } + if (randomValue != null) { + matomoRequest.setRandomValue(randomValue); + } + if (referrerUrl != null) { + matomoRequest.setReferrerUrl(referrerUrl); + } + if (requestDatetime != null) { + matomoRequest.setRequestDatetime(requestDatetime); + } + if (required != null) { + matomoRequest.setRequired(required); + } + if (responseAsImage != null) { + matomoRequest.setResponseAsImage(responseAsImage); + } + if (searchCategory != null) { + matomoRequest.setSearchCategory(searchCategory); + } + if (searchQuery != null) { + matomoRequest.setSearchQuery(searchQuery); + } + if (searchResultsCount != null) { + matomoRequest.setSearchResultsCount(searchResultsCount); + } + if (trackBotRequests != null) { + matomoRequest.setTrackBotRequests(trackBotRequests); + } + if (visitCustomVariables != null) { + for (int i = 0; i < visitCustomVariables.size(); i++) { + CustomVariable visitCustomVariable = visitCustomVariables.get(i); + matomoRequest.setVisitCustomVariable(visitCustomVariable, i + 1); + } + } + if (userId != null) { + matomoRequest.setUserId(userId); + } + if (visitorCity != null) { + matomoRequest.setVisitorCity(visitorCity); + } + if (visitorCountry != null) { + matomoRequest.setVisitorCountry(visitorCountry); + } + if (visitorCustomId != null) { + matomoRequest.setVisitorCustomId(visitorCustomId); + } + if (visitorFirstVisitTimestamp != null) { + matomoRequest.setVisitorFirstVisitTimestamp(visitorFirstVisitTimestamp); + } + if (visitorId != null) { + matomoRequest.setVisitorId(visitorId); + } + if (visitorIp != null) { + matomoRequest.setVisitorIp(visitorIp); + } + if (visitorLatitude != null) { + matomoRequest.setVisitorLatitude(visitorLatitude); + } + if (visitorLongitude != null) { + matomoRequest.setVisitorLongitude(visitorLongitude); + } + if (visitorPreviousVisitTimestamp != null) { + matomoRequest.setVisitorPreviousVisitTimestamp(visitorPreviousVisitTimestamp); + } + if (visitorRegion != null) { + matomoRequest.setVisitorRegion(visitorRegion); + } + if (visitorVisitCount != null) { + matomoRequest.setVisitorVisitCount(visitorVisitCount); + } + return matomoRequest; + } + +} diff --git a/src/main/java/org/matomo/java/tracking/MatomoTracker.java b/src/main/java/org/matomo/java/tracking/MatomoTracker.java new file mode 100644 index 00000000..4b0d8882 --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/MatomoTracker.java @@ -0,0 +1,271 @@ +/* + * Matomo Java Tracker + * + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause + */ +package org.matomo.java.tracking; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.extern.slf4j.Slf4j; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.concurrent.FutureCallback; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.concurrent.Future; + +/** + * A class that sends {@link MatomoRequest}s to a specified Matomo server. + * + * @author brettcsorba + */ +@Slf4j +public class MatomoTracker { + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + private static final String AUTH_TOKEN = "token_auth"; + private static final String REQUESTS = "requests"; + private static final int DEFAULT_TIMEOUT = 5000; + private final URI hostUrl; + private final int timeout; + private final String proxyHost; + private final int proxyPort; + + /** + * Creates a tracker that will send {@link MatomoRequest}s to the specified + * Tracking HTTP API endpoint. + * + * @param hostUrl url endpoint to send requests to. Usually in the format + * http://your-matomo-domain.tld/matomo.php. + */ + public MatomoTracker(final String hostUrl) { + this(hostUrl, DEFAULT_TIMEOUT); + } + + /** + * Creates a tracker that will send {@link MatomoRequest}s to the specified + * Tracking HTTP API endpoint. + * + * @param hostUrl url endpoint to send requests to. Usually in the format + * http://your-matomo-domain.tld/matomo.php. + * @param timeout the timeout of the sent request in milliseconds + */ + public MatomoTracker(final String hostUrl, final int timeout) { + this(hostUrl, null, 0, timeout); + } + + public MatomoTracker(final String hostUrl, final String proxyHost, final int proxyPort, final int timeout) { + this.hostUrl = URI.create(hostUrl); + this.proxyHost = proxyHost; + this.proxyPort = proxyPort; + this.timeout = timeout; + } + + /** + * Creates a tracker that will send {@link MatomoRequest}s to the specified + * Tracking HTTP API endpoint via the provided proxy + * + * @param hostUrl url endpoint to send requests to. Usually in the format + * http://your-matomo-domain.tld/matomo.php. + * @param proxyHost url endpoint for the proxy + * @param proxyPort proxy server port number + */ + public MatomoTracker(final String hostUrl, final String proxyHost, final int proxyPort) { + this(hostUrl, proxyHost, proxyPort, DEFAULT_TIMEOUT); + } + + /** + * Send a request. + * + * @param request request to send + * @return the response from this request + * @throws IOException thrown if there was a problem with this connection + * @deprecated use sendRequestAsync instead + */ + @Deprecated + public HttpResponse sendRequest(final MatomoRequest request) throws IOException { + final HttpClient client = getHttpClient(); + try { + HttpUriRequest get = new HttpGet(new URIBuilder(hostUrl).addParameters(QueryParameters.fromMap(request.getParameters())).build()); + log.debug("Sending request via GET: {}", request); + return client.execute(get); + } catch (final URISyntaxException e) { + throw new IOException(e); + } + } + + /** + * Get a HTTP client. With proxy if a proxy is provided in the constructor. + * + * @return a HTTP client + */ + protected HttpClient getHttpClient() { + return HttpClientFactory.getInstanceFor(proxyHost, proxyPort, timeout); + } + + /** + * Send a request. + * + * @param request request to send + * @return future with response from this request + * @throws IOException thrown if there was a problem with this connection + */ + public Future sendRequestAsync(final MatomoRequest request) throws IOException { + return sendRequestAsync(request, null); + } + + /** + * Send a request. + * + * @param request request to send + * @param callback callback that gets executed when response arrives + * @return future with response from this request + * @throws IOException thrown if there was a problem with this connection + */ + public Future sendRequestAsync(final MatomoRequest request, FutureCallback callback) throws IOException { + final CloseableHttpAsyncClient client = getHttpAsyncClient(); + client.start(); + + try { + HttpUriRequest get = new HttpGet(new URIBuilder(hostUrl).addParameters(QueryParameters.fromMap(request.getParameters())).build()); + log.debug("Sending async request via GET: {}", request); + return client.execute(get, callback); + } catch (final URISyntaxException e) { + throw new IOException(e); + } + } + + /** + * Get an async HTTP client. With proxy if a proxy is provided in the constructor. + * + * @return an async HTTP client + */ + protected CloseableHttpAsyncClient getHttpAsyncClient() { + return HttpClientFactory.getAsyncInstanceFor(proxyHost, proxyPort, timeout); + } + + /** + * Send multiple requests in a single HTTP call. More efficient than sending + * several individual requests. + * + * @param requests the requests to send + * @return the response from these requests + * @throws IOException thrown if there was a problem with this connection + * @deprecated use sendBulkRequestAsync instead + */ + @Deprecated + public HttpResponse sendBulkRequest(final Iterable requests) throws IOException { + return sendBulkRequest(requests, null); + } + + /** + * Send multiple requests in a single HTTP call. More efficient than sending + * several individual requests. Specify the AuthToken if parameters that require + * an auth token is used. + * + * @param requests the requests to send + * @param authToken specify if any of the parameters use require AuthToken + * @return the response from these requests + * @throws IOException thrown if there was a problem with this connection + * @deprecated use sendBulkRequestAsync instead + */ + @Deprecated + public HttpResponse sendBulkRequest(final Iterable requests, final String authToken) throws IOException { + if (authToken != null && authToken.length() != MatomoRequest.AUTH_TOKEN_LENGTH) { + throw new IllegalArgumentException(authToken + " is not " + MatomoRequest.AUTH_TOKEN_LENGTH + " characters long."); + } + + HttpPost post = buildPost(requests, authToken); + final HttpClient client = getHttpClient(); + log.debug("Sending requests via POST: {}", requests); + return client.execute(post); + } + + private HttpPost buildPost(Iterable requests, String authToken) { + ObjectNode objectNode = OBJECT_MAPPER.createObjectNode(); + ArrayNode requestsNode = objectNode.putArray(REQUESTS); + for (final MatomoRequest request : requests) { + requestsNode.add(new URIBuilder().addParameters(QueryParameters.fromMap(request.getParameters())).toString()); + } + if (authToken != null) { + objectNode.put(AUTH_TOKEN, authToken); + } + HttpPost post = new HttpPost(hostUrl); + post.setEntity(new StringEntity(objectNode.toString(), ContentType.APPLICATION_JSON)); + return post; + } + + /** + * Send multiple requests in a single HTTP call. More efficient than sending + * several individual requests. + * + * @param requests the requests to send + * @return future with response from these requests + * @throws IOException thrown if there was a problem with this connection + */ + public Future sendBulkRequestAsync(final Iterable requests) throws IOException { + return sendBulkRequestAsync(requests, null, null); + } + + /** + * Send multiple requests in a single HTTP call. More efficient than sending + * several individual requests. Specify the AuthToken if parameters that require + * an auth token is used. + * + * @param requests the requests to send + * @param authToken specify if any of the parameters use require AuthToken + * @param callback callback that gets executed when response arrives + * @return the response from these requests + * @throws IOException thrown if there was a problem with this connection + */ + public Future sendBulkRequestAsync(final Iterable requests, final String authToken, FutureCallback callback) throws IOException { + if (authToken != null && authToken.length() != MatomoRequest.AUTH_TOKEN_LENGTH) { + throw new IllegalArgumentException(authToken + " is not " + MatomoRequest.AUTH_TOKEN_LENGTH + " characters long."); + } + HttpPost post = buildPost(requests, authToken); + final CloseableHttpAsyncClient client = getHttpAsyncClient(); + client.start(); + log.debug("Sending async requests via POST: {}", requests); + return client.execute(post, callback); + } + + /** + * Send multiple requests in a single HTTP call. More efficient than sending + * several individual requests. + * + * @param requests the requests to send + * @param callback callback that gets executed when response arrives + * @return future with response from these requests + * @throws IOException thrown if there was a problem with this connection + */ + public Future sendBulkRequestAsync(final Iterable requests, FutureCallback callback) throws IOException { + return sendBulkRequestAsync(requests, null, callback); + } + + /** + * Send multiple requests in a single HTTP call. More efficient than sending + * several individual requests. Specify the AuthToken if parameters that require + * an auth token is used. + * + * @param requests the requests to send + * @param authToken specify if any of the parameters use require AuthToken + * @return the response from these requests + * @throws IOException thrown if there was a problem with this connection + */ + public Future sendBulkRequestAsync(final Iterable requests, final String authToken) throws IOException { + return sendBulkRequestAsync(requests, authToken, null); + } +} diff --git a/src/main/java/org/matomo/java/tracking/QueryParameters.java b/src/main/java/org/matomo/java/tracking/QueryParameters.java new file mode 100644 index 00000000..2e1f94c3 --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/QueryParameters.java @@ -0,0 +1,32 @@ +package org.matomo.java.tracking; + +import lombok.NonNull; +import org.apache.http.NameValuePair; +import org.apache.http.message.BasicNameValuePair; + +import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +public final class QueryParameters { + + private QueryParameters() { + // utility + } + + @Nonnull + public static List fromMap(@NonNull Map> map) { + List queryParameters = new ArrayList<>(); + for (Map.Entry> entries : map.entrySet()) { + for (Object value : entries.getValue()) { + queryParameters.add(new BasicNameValuePair(entries.getKey(), value.toString())); + } + } + queryParameters.sort(Comparator.comparing(NameValuePair::getName)); + return queryParameters; + } + +} diff --git a/src/main/java/org/piwik/java/tracking/CustomVariable.java b/src/main/java/org/piwik/java/tracking/CustomVariable.java index 392b7473..48b3813a 100644 --- a/src/main/java/org/piwik/java/tracking/CustomVariable.java +++ b/src/main/java/org/piwik/java/tracking/CustomVariable.java @@ -1,52 +1,25 @@ /* - * Piwik Java Tracker + * Matomo Java Tracker * - * @link https://github.com/piwik/piwik-java-tracker - * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause */ package org.piwik.java.tracking; +import org.matomo.java.tracking.MatomoRequest; + /** - * A user defined custom variable. - * * @author brettcsorba + * @deprecated Use {@link org.matomo.java.tracking.CustomVariable} instead. */ -public final class CustomVariable { - private final String key; - private final String value; +@Deprecated +public class CustomVariable extends org.matomo.java.tracking.CustomVariable { /** - * Create a new CustomVariable - * - * @param key the key of this CustomVariable - * @param value the value of this CustomVariable + * @deprecated Use {@link MatomoRequest} instead. */ + @Deprecated public CustomVariable(String key, String value) { - if (key == null) { - throw new NullPointerException("Key cannot be null."); - } - if (value == null) { - throw new NullPointerException("Value cannot be null."); - } - this.key = key; - this.value = value; - } - - /** - * Get the key of this CustomVariable - * - * @return the key of this CustomVariable - */ - public String getKey() { - return key; - } - - /** - * Get the value of this CustomVariable - * - * @return the value of this CustomVariable - */ - public String getValue() { - return value; + super(key, value); } } diff --git a/src/main/java/org/piwik/java/tracking/CustomVariableList.java b/src/main/java/org/piwik/java/tracking/CustomVariableList.java deleted file mode 100644 index a8ade45f..00000000 --- a/src/main/java/org/piwik/java/tracking/CustomVariableList.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Piwik Java Tracker - * - * @link https://github.com/piwik/piwik-java-tracker - * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause - */ -package org.piwik.java.tracking; - -import javax.json.Json; -import javax.json.JsonArrayBuilder; -import javax.json.JsonObjectBuilder; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.Map.Entry; - -/** - * @author brettcsorba - */ -class CustomVariableList { - private final Map map = new HashMap<>(); - - void add(CustomVariable cv) { - boolean found = false; - for (Entry entry : map.entrySet()) { - if (entry.getValue().getKey().equals(cv.getKey())) { - map.put(entry.getKey(), cv); - found = true; - } - } - if (!found) { - int i = 1; - while (map.containsKey(i)) { - ++i; - } - - map.put(i, cv); - } - } - - void add(CustomVariable cv, int index) { - if (index <= 0) { - throw new IllegalArgumentException("Index must be greater than 0."); - } - map.put(index, cv); - } - - CustomVariable get(int index) { - if (index <= 0) { - throw new IllegalArgumentException("Index must be greater than 0."); - } - return map.get(index); - } - - String get(String key) { - Iterator> i = map.entrySet().iterator(); - while (i.hasNext()) { - CustomVariable value = i.next().getValue(); - if (value.getKey().equals(key)) { - return value.getValue(); - } - } - return null; - } - - void remove(int index) { - map.remove(index); - } - - void remove(String key) { - Iterator> i = map.entrySet().iterator(); - while (i.hasNext()) { - Entry entry = i.next(); - if (entry.getValue().getKey().equals(key)) { - i.remove(); - } - } - } - - boolean isEmpty() { - return map.isEmpty(); - } - - @Override - public String toString() { - JsonObjectBuilder ob = Json.createObjectBuilder(); - - for (Entry entry : map.entrySet()) { - JsonArrayBuilder ab = Json.createArrayBuilder(); - ab.add(entry.getValue().getKey()); - ab.add(entry.getValue().getValue()); - ob.add(entry.getKey().toString(), ab); - } - - return ob.build().toString(); - } -} diff --git a/src/main/java/org/piwik/java/tracking/EcommerceItem.java b/src/main/java/org/piwik/java/tracking/EcommerceItem.java index 616d2e63..65b21446 100644 --- a/src/main/java/org/piwik/java/tracking/EcommerceItem.java +++ b/src/main/java/org/piwik/java/tracking/EcommerceItem.java @@ -1,169 +1,25 @@ /* - * Piwik Java Tracker + * Matomo Java Tracker * - * @link https://github.com/piwik/piwik-java-tracker - * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause */ package org.piwik.java.tracking; -import javax.json.Json; -import javax.json.JsonArray; -import javax.json.JsonArrayBuilder; -import javax.json.JsonValue; +import org.matomo.java.tracking.MatomoRequest; /** - * Represents an item in an ecommerce order. - * * @author brettcsorba + * @deprecated Use {@link org.matomo.java.tracking.EcommerceItem} instead. */ -public class EcommerceItem implements JsonValue { - private String sku; - private String name; - private String category; - private Double price; - private Integer quantity; +@Deprecated +public class EcommerceItem extends org.matomo.java.tracking.EcommerceItem { /** - * Construct an EcommerceItem from its sku, name, category, price, and - * quantity of the order. - * - * @param sku the item's sku - * @param name the item's name - * @param category the item's category - * @param price the item's price - * @param quantity the quantity of this item in this order + * @deprecated Use {@link MatomoRequest} instead. */ + @Deprecated public EcommerceItem(String sku, String name, String category, Double price, Integer quantity) { - this.sku = sku; - this.name = name; - this.category = category; - this.price = price; - this.quantity = quantity; - } - - /** - * Get an item's sku. - * - * @return the item's sku - */ - public String getSku() { - return sku; - } - - /** - * Set an item's sku. - * - * @param sku the sku to set - */ - public void setSku(String sku) { - this.sku = sku; - } - - /** - * Get an item's name. - * - * @return the item's name - */ - public String getName() { - return name; - } - - /** - * Set an item's name. - * - * @param name the name to set - */ - public void setName(String name) { - this.name = name; - } - - /** - * Get an item's category. - * - * @return an item's category - */ - public String getCategory() { - return category; - } - - /** - * Set an item's category. - * - * @param category the category to set - */ - public void setCategory(String category) { - this.category = category; - } - - /** - * Get an item's price. - * - * @return an item's price - */ - public Double getPrice() { - return price; - } - - /** - * Set an item's price. - * - * @param price the price to set - */ - public void setPrice(Double price) { - this.price = price; - } - - /** - * Get the quantity of this item in this order. - * - * @return the quantity of this item in the order - */ - public Integer getQuantity() { - return quantity; - } - - /** - * Set the quantity of this item in this order - * - * @param quantity the quantity of this item to set - */ - public void setQuantity(Integer quantity) { - this.quantity = quantity; - } - - /** - * Get the JSON value type of EcommerceItem. - * - * @return ValueType.ARRAY - */ - @Override - public ValueType getValueType() { - return ValueType.ARRAY; - } - - /** - * Returns the value of this EcommerceItem as a JSON Array string. - * - * @return this as a JSON array string - */ - @Override - public String toString() { - return toJsonArray().toString(); - } - - /** - * Returns the value of this EcommerceItem as a JsonArray. - * - * @return this as a JsonArray - */ - JsonArray toJsonArray() { - JsonArrayBuilder ab = Json.createArrayBuilder(); - ab.add(sku); - ab.add(name); - ab.add(category); - ab.add(price); - ab.add(quantity); - - return ab.build(); + super(sku, name, category, price, quantity); } } diff --git a/src/main/java/org/piwik/java/tracking/HttpClientFactory.java b/src/main/java/org/piwik/java/tracking/HttpClientFactory.java deleted file mode 100644 index 73c5f196..00000000 --- a/src/main/java/org/piwik/java/tracking/HttpClientFactory.java +++ /dev/null @@ -1,104 +0,0 @@ -package org.piwik.java.tracking; - -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import org.apache.http.HttpHost; -import org.apache.http.client.HttpClient; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.DefaultProxyRoutePlanner; -import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; -import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; - -/** - * Internal factory for providing instances of HTTP clients. - * Especially {@linkAsyncHttpClient} instances are intended to be global resources that share the same lifecycle as the application. - * For details see Apache documentation. - * - * @author norbertroamsys - */ -class HttpClientFactory { - - /** - * Internal key class for caching {@link CloseableHttpAsyncClient} instances. - */ - private static final class KeyEntry { - - private final String proxyHost; - private final int proxyPort, timeout; - - public KeyEntry(final String proxyHost, final int proxyPort, final int timeout) { - this.proxyHost = proxyHost; - this.proxyPort = proxyPort; - this.timeout = timeout; - } - - @Override - public int hashCode() { - return Objects.hash(proxyHost, proxyPort, timeout); - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } else if (obj == null) { - return false; - } else if (obj instanceof KeyEntry) { - final KeyEntry other = (KeyEntry) obj; - return Objects.equals(proxyHost, other.proxyHost) && proxyPort == other.proxyPort && timeout == other.timeout; - } else { - return false; - } - } - } - - private static final Map ASYNC_INSTANCES = new HashMap<>(); - - /** - * Factory for getting a synchronous client by proxy and timeout configuration. - * The clients will be created on each call. - * - * @param proxyHost the proxy host - * @param proxyPort the proxy port - * @param timeout the timeout - * @return the created client - */ - public static HttpClient getInstanceFor(final String proxyHost, final int proxyPort, final int timeout) { - return HttpClientBuilder.create().setRoutePlanner(createRoutePlanner(proxyHost, proxyPort)).setDefaultRequestConfig(createRequestConfig(timeout)).build(); - } - - /** - * Factory for getting a asynchronous client by proxy and timeout configuration. - * The clients will be created and cached as a singleton instance. - * - * @param proxyHost the proxy host - * @param proxyPort the proxy port - * @param timeout the timeout - * @return the created client - */ - public static synchronized CloseableHttpAsyncClient getAsyncInstanceFor(final String proxyHost, final int proxyPort, final int timeout) { - return ASYNC_INSTANCES.computeIfAbsent(new KeyEntry(proxyHost, proxyPort, timeout), key -> - HttpAsyncClientBuilder.create().setRoutePlanner(createRoutePlanner(key.proxyHost, key.proxyPort)).setDefaultRequestConfig(createRequestConfig(key.timeout)).build()); - } - - private static DefaultProxyRoutePlanner createRoutePlanner(final String proxyHost, final int proxyPort) { - if (proxyHost != null && proxyPort != 0) { - final HttpHost proxy = new HttpHost(proxyHost, proxyPort); - final DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy); - return routePlanner; - } else { - return null; - } - } - - private static RequestConfig createRequestConfig(final int timeout) { - final RequestConfig.Builder config = RequestConfig.custom() - .setConnectTimeout(timeout) - .setConnectionRequestTimeout(timeout) - .setSocketTimeout(timeout); - return config.build(); - } - -} diff --git a/src/main/java/org/piwik/java/tracking/PiwikDate.java b/src/main/java/org/piwik/java/tracking/PiwikDate.java index 66fe885c..6105bd8d 100644 --- a/src/main/java/org/piwik/java/tracking/PiwikDate.java +++ b/src/main/java/org/piwik/java/tracking/PiwikDate.java @@ -1,65 +1,46 @@ /* - * Piwik Java Tracker + * Matomo Java Tracker * - * @link https://github.com/piwik/piwik-java-tracker - * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause */ package org.piwik.java.tracking; -import java.text.SimpleDateFormat; -import java.util.Date; +import org.matomo.java.tracking.MatomoDate; + +import java.time.ZoneId; import java.util.TimeZone; /** - * A datetime object that will return the datetime in the format {@code yyyy-MM-dd hh:mm:ss}. - * * @author brettcsorba + * @deprecated Use {@link MatomoDate} instead. */ -public class PiwikDate extends Date { - SimpleDateFormat format; +@Deprecated +public class PiwikDate extends MatomoDate { /** - * Allocates a Date object and initializes it so that it represents the time - * at which it was allocated, measured to the nearest millisecond. + * @author brettcsorba + * @deprecated Use {@link MatomoDate} instead. */ public PiwikDate() { super(); - format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - format.setTimeZone(TimeZone.getTimeZone("UTC")); } /** - * Allocates a Date object and initializes it to represent the specified number - * of milliseconds since the standard base time known as "the epoch", namely - * January 1, 1970, 00:00:00 GMT. - * - * @param date the milliseconds since January 1, 1970, 00:00:00 GMT. + * @author brettcsorba + * @deprecated Use {@link MatomoDate} instead. */ - public PiwikDate(long date) { - super(date); - format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - format.setTimeZone(TimeZone.getTimeZone("UTC")); + public PiwikDate(long epochMilli) { + super(epochMilli); } /** - * Sets the time zone of the String that will be returned by {@link #toString()}. - * Defaults to UTC. - * - * @param zone the TimeZone to set + * @author brettcsorba + * @deprecated Use {@link MatomoDate#setTimeZone(ZoneId)} instead. */ + @Deprecated public void setTimeZone(TimeZone zone) { - format.setTimeZone(zone); + setTimeZone(zone.toZoneId()); } - /** - * Converts this PiwikDate object to a String of the form:
- *
- * {@code yyyy-MM-dd hh:mm:ss}. - * - * @return a string representation of this PiwikDate - */ - @Override - public String toString() { - return format.format(this); - } } diff --git a/src/main/java/org/piwik/java/tracking/PiwikJsonArray.java b/src/main/java/org/piwik/java/tracking/PiwikJsonArray.java deleted file mode 100644 index bdf9e6ee..00000000 --- a/src/main/java/org/piwik/java/tracking/PiwikJsonArray.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Piwik Java Tracker - * - * @link https://github.com/piwik/piwik-java-tracker - * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause - */ -package org.piwik.java.tracking; - -import javax.json.Json; -import javax.json.JsonArrayBuilder; -import javax.json.JsonValue; -import java.util.ArrayList; -import java.util.List; - -/** - * Object representing a JSON array required by some Piwik query parameters. - * - * @author brettcsorba - */ -public class PiwikJsonArray { - List list = new ArrayList<>(); - - /** - * Get the value stored at the specified index. - * - * @param index the index of the value to return - * @return the value stored at the specified index - */ - public JsonValue get(int index) { - return list.get(index); - } - - /** - * Add a value to the end of this array. - * - * @param value value to add to the end of the array - */ - public void add(JsonValue value) { - list.add(value); - } - - /** - * Set the value at the specified index to the specified value. - * - * @param index the index of the value to set - * @param value the value to set at the specified index - */ - public void set(int index, JsonValue value) { - list.set(index, value); - } - - /** - * Returns a JSON encoded array string representing this object. - * - * @return returns the current array as a JSON encode string - */ - @Override - public String toString() { - JsonArrayBuilder ab = Json.createArrayBuilder(); - - for (int x = 0; x < list.size(); ++x) { - JsonValue value = list.get(x); - if (value instanceof EcommerceItem) { - ab.add(((EcommerceItem) value).toJsonArray()); - } else { - ab.add(value); - } - } - - return ab.build().toString(); - } -} diff --git a/src/main/java/org/piwik/java/tracking/PiwikJsonObject.java b/src/main/java/org/piwik/java/tracking/PiwikJsonObject.java deleted file mode 100644 index 0b83a651..00000000 --- a/src/main/java/org/piwik/java/tracking/PiwikJsonObject.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Piwik Java Tracker - * - * @link https://github.com/piwik/piwik-java-tracker - * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause - */ -package org.piwik.java.tracking; - -import javax.json.Json; -import javax.json.JsonArrayBuilder; -import javax.json.JsonObjectBuilder; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Map.Entry; - -/** - * Object representing the custom variable array required by some - * Piwik query parameters. An array is represented by an object that has - * the index of the entry as the key (1 indexed) and a 2 entry array representing - * a custom key and custom value as the value. - * - * @author brettcsorba - */ -@Deprecated -public class PiwikJsonObject { - Map map = new LinkedHashMap<>(); - - /** - * Gets the custom value stored at this custom key. - * - * @param key key used to lookup value - * @return value stored at specified key, null if not present - */ - public String get(String key) { - return map.get(key); - } - - /** - * Returns true if this object contains no custom key-value pairs. - * - * @return true if this object contains no custom key-value pairs - */ - public boolean isEmpty() { - return size() == 0; - } - - /** - * Puts a custom value at this custom key. - * - * @param key key to store value at - * @param value value to store at specified key - * @return previous value stored at key if present, null otherwise - */ - public String put(String key, String value) { - return map.put(key, value); - } - - /** - * Removes the custom value stored at this custom key. - * - * @param key key used to lookup value to remove - * @return the value that was removed, null if no value was there to remove - */ - public String remove(String key) { - return map.remove(key); - } - - /** - * Returns the number of custom key-value pairs. - * - * @return the number of custom key-value pairs - */ - public int size() { - return map.size(); - } - - /** - * Produces the JSON string representing this object.
- *
- * For example, if the following values were put into the object
- *
- * {@code ("key1", "value1") } and {@code ("key2", "value2") }
- *
- * {@code {"1": ["key1", "value1"], "2": ["key2": "value2"]} }
- *
- * would be produced. The produced JSON will be ordered according to the - * order the values were put in. Removing an object will cause the values - * to backfill accordingly.
- *
- * For example, if the following values were put into the object
- *
- * {@code ("key1", "value1")}, {@code ("key2", "value2")}, and {@code ("key3", "value3")}
- *
- * and {@code ("key2", "value2") } was then removed, then
- *
- * {@code {"1": ["key1", "value1"], "2": ["key3": "value3"]} }
- *
- * would be produced. - *
- * - * @return the JSON string representation of this object - */ - @Override - public String toString() { - JsonObjectBuilder ob = Json.createObjectBuilder(); - - int x = 1; - for (Entry entry : map.entrySet()) { - JsonArrayBuilder ab = Json.createArrayBuilder(); - ab.add(entry.getKey()); - ab.add(entry.getValue()); - ob.add(Integer.toString(x++), ab); - } - - return ob.build().toString(); - } -} diff --git a/src/main/java/org/piwik/java/tracking/PiwikLocale.java b/src/main/java/org/piwik/java/tracking/PiwikLocale.java index a573026a..686bbfd7 100644 --- a/src/main/java/org/piwik/java/tracking/PiwikLocale.java +++ b/src/main/java/org/piwik/java/tracking/PiwikLocale.java @@ -1,55 +1,27 @@ /* - * Piwik Java Tracker + * Matomo Java Tracker * - * @link https://github.com/piwik/piwik-java-tracker - * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause */ package org.piwik.java.tracking; +import org.matomo.java.tracking.MatomoLocale; + import java.util.Locale; /** - * Object representing a locale required by some Piwik query parameters. - * * @author brettcsorba + * @deprecated Use {@link org.matomo.java.tracking.MatomoLocale} instead. */ -public class PiwikLocale { - private Locale locale; +@Deprecated +public class PiwikLocale extends MatomoLocale { /** - * Create this PiwikLocale from a Locale. - * - * @param locale the locale to create this object from + * @deprecated Use {@link MatomoLocale} instead. */ + @Deprecated public PiwikLocale(Locale locale) { - this.locale = locale; - } - - /** - * Gets the locale. - * - * @return the locale - */ - public Locale getLocale() { - return locale; - } - - /** - * Sets the locale. - * - * @param locale the locale to set - */ - public void setLocale(Locale locale) { - this.locale = locale; - } - - /** - * Returns the locale's lowercase country code. - * - * @return the locale's lowercase country code - */ - @Override - public String toString() { - return locale.getCountry().toLowerCase(); + super(locale); } } diff --git a/src/main/java/org/piwik/java/tracking/PiwikRequest.java b/src/main/java/org/piwik/java/tracking/PiwikRequest.java index d1d32440..b7ad9181 100644 --- a/src/main/java/org/piwik/java/tracking/PiwikRequest.java +++ b/src/main/java/org/piwik/java/tracking/PiwikRequest.java @@ -1,2115 +1,27 @@ /* - * Piwik Java Tracker + * Matomo Java Tracker * - * @link https://github.com/piwik/piwik-java-tracker - * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause */ package org.piwik.java.tracking; -import javax.json.JsonValue; -import javax.xml.bind.DatatypeConverter; -import javax.xml.bind.TypeConstraintException; -import java.io.UnsupportedEncodingException; +import org.matomo.java.tracking.MatomoRequest; + import java.net.URL; -import java.net.URLEncoder; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Random; /** - * A class that implements the - * Piwik Tracking HTTP API. These requests can be sent using {@link PiwikTracker}. - * * @author brettcsorba + * @deprecated Use {@link MatomoRequest} instead. */ -public class PiwikRequest { - public static final int ID_LENGTH = 16; - public static final int AUTH_TOKEN_LENGTH = 32; - - private static final String ACTION_NAME = "action_name"; - private static final String ACTION_TIME = "gt_ms"; - private static final String ACTION_URL = "url"; - private static final String API_VERSION = "apiv"; - private static final String AUTH_TOKEN = "token_auth"; - private static final String CAMPAIGN_KEYWORD = "_rck"; - private static final String CAMPAIGN_NAME = "_rcn"; - private static final String CHARACTER_SET = "cs"; - private static final String CONTENT_INTERACTION = "c_i"; - private static final String CONTENT_NAME = "c_n"; - private static final String CONTENT_PIECE = "c_p"; - private static final String CONTENT_TARGET = "c_t"; - private static final String CURRENT_HOUR = "h"; - private static final String CURRENT_MINUTE = "m"; - private static final String CURRENT_SECOND = "s"; - private static final String DEVICE_RESOLUTION = "res"; - private static final String DOWNLOAD_URL = "download"; - private static final String ECOMMERCE_DISCOUNT = "ec_dt"; - private static final String ECOMMERCE_ID = "ec_id"; - private static final String ECOMMERCE_ITEMS = "ec_items"; - private static final String ECOMMERCE_LAST_ORDER_TIMESTAMP = "_ects"; - private static final String ECOMMERCE_REVENUE = "revenue"; - private static final String ECOMMERCE_SHIPPING_COST = "ec_sh"; - private static final String ECOMMERCE_SUBTOTAL = "ec_st"; - private static final String ECOMMERCE_TAX = "ec_tx"; - private static final String EVENT_ACTION = "e_a"; - private static final String EVENT_CATEGORY = "e_c"; - private static final String EVENT_NAME = "e_n"; - private static final String EVENT_VALUE = "e_v"; - private static final String HEADER_ACCEPT_LANGUAGE = "lang"; - private static final String GOAL_ID = "idgoal"; - private static final String GOAL_REVENUE = "revenue"; - private static final String HEADER_USER_AGENT = "ua"; - private static final String NEW_VISIT = "new_visit"; - private static final String OUTLINK_URL = "link"; - private static final String PAGE_CUSTOM_VARIABLE = "cvar"; - private static final String PLUGIN_DIRECTOR = "dir"; - private static final String PLUGIN_FLASH = "fla"; - private static final String PLUGIN_GEARS = "gears"; - private static final String PLUGIN_JAVA = "java"; - private static final String PLUGIN_PDF = "pdf"; - private static final String PLUGIN_QUICKTIME = "qt"; - private static final String PLUGIN_REAL_PLAYER = "realp"; - private static final String PLUGIN_SILVERLIGHT = "ag"; - private static final String PLUGIN_WINDOWS_MEDIA = "wma"; - private static final String RANDOM_VALUE = "rand"; - private static final String REFERRER_URL = "urlref"; - private static final String REQUEST_DATETIME = "cdt"; - private static final String REQUIRED = "rec"; - private static final String RESPONSE_AS_IMAGE = "send_image"; - private static final String SEARCH_CATEGORY = "search_cat"; - private static final String SEARCH_QUERY = "search"; - private static final String SEARCH_RESULTS_COUNT = "search_count"; - private static final String SITE_ID = "idsite"; - private static final String TRACK_BOT_REQUESTS = "bots"; - private static final String VISIT_CUSTOM_VARIABLE = "_cvar"; - private static final String USER_ID = "uid"; - private static final String VISITOR_CITY = "city"; - private static final String VISITOR_COUNTRY = "country"; - private static final String VISITOR_CUSTOM_ID = "cid"; - private static final String VISITOR_FIRST_VISIT_TIMESTAMP = "_idts"; - private static final String VISITOR_ID = "_id"; - private static final String VISITOR_IP = "cip"; - private static final String VISITOR_LATITUDE = "lat"; - private static final String VISITOR_LONGITUDE = "long"; - private static final String VISITOR_PREVIOUS_VISIT_TIMESTAMP = "_viewts"; - private static final String VISITOR_REGION = "region"; - private static final String VISITOR_VISIT_COUNT = "_idvc"; - - private static final int RANDOM_VALUE_LENGTH = 20; - private static final long REQUEST_DATETIME_AUTH_LIMIT = 14400000L; - - private final Map parameters = new HashMap<>(); - private final Map customTrackingParameters = new HashMap<>(); +@Deprecated +public class PiwikRequest extends MatomoRequest { /** - * Create a new request from the id of the site being tracked and the full - * url for the current action. This constructor also sets: - *
-   * {@code
-   * Required = true
-   * Visior Id = random 16 character hex string
-   * Random Value = random 20 character hex string
-   * API version = 1
-   * Response as Image = false
-   * }
-   * 
- * Overwrite these values yourself as desired. - * - * @param siteId the id of the website we're tracking a visit/action for - * @param actionUrl the full URL for the current action + * @deprecated Use {@link MatomoRequest} instead. */ + @Deprecated public PiwikRequest(Integer siteId, URL actionUrl) { - setParameter(SITE_ID, siteId); - setBooleanParameter(REQUIRED, true); - setParameter(ACTION_URL, actionUrl); - setParameter(VISITOR_ID, getRandomHexString(ID_LENGTH)); - setParameter(RANDOM_VALUE, getRandomHexString(RANDOM_VALUE_LENGTH)); - setParameter(API_VERSION, "1"); - setBooleanParameter(RESPONSE_AS_IMAGE, false); - } - - /** - * Get the title of the action being tracked - * - * @return the title of the action being tracked - */ - - public String getActionName() { - return (String) getParameter(ACTION_NAME); - } - - /** - * Set the title of the action being tracked. It is possible to - * use slashes / - * to set one or several categories for this action. - * For example, Help / Feedback - * will create the Action Feedback in the category Help. - * - * @param actionName the title of the action to set. A null value will remove this parameter - */ - public void setActionName(String actionName) { - setParameter(ACTION_NAME, actionName); - } - - /** - * Get the amount of time it took the server to generate this action, in milliseconds. - * - * @return the amount of time - */ - public Long getActionTime() { - return (Long) getParameter(ACTION_TIME); - } - - /** - * Set the amount of time it took the server to generate this action, in milliseconds. - * This value is used to process the - * Page speed report - * Avg. generation time column in the Page URL and Page Title reports, - * as well as a site wide running average of the speed of your server. - * - * @param actionTime the amount of time to set. A null value will remove this parameter - */ - public void setActionTime(Long actionTime) { - setParameter(ACTION_TIME, actionTime); - } - - /** - * Get the full URL for the current action. - * - * @return the full URL - * @throws TypeConstraintException if the stored Action URL is a string - */ - public URL getActionUrl() { - return returnAsUrl(ACTION_URL, "Action URL", "getActionUrlAsString"); - } - - /** - * Get the full URL for the current action. - * - * @return the full URL - * @throws TypeConstraintException if the stored Action URL is a URL - */ - public String getActionUrlAsString() { - return returnAsString(ACTION_URL, "Action URL", "getActionUrl"); - } - - /** - * Set the full URL for the current action. - * - * @param actionUrl the full URL to set. A null value will remove this parameter - */ - public void setActionUrl(URL actionUrl) { - setParameter(ACTION_URL, actionUrl); - } - - /** - * Set the full URL for the current action. - * - * @param actionUrl the full URL to set. A null value will remove this parameter - */ - public void setActionUrlWithString(String actionUrl) { - setParameter(ACTION_URL, actionUrl); - } - - /** - * Get the api version - * - * @return the api version - */ - public String getApiVersion() { - return (String) getParameter(API_VERSION); - } - - /** - * Set the api version to use (currently always set to 1) - * - * @param apiVersion the api version to set. A null value will remove this parameter - */ - public void setApiVersion(String apiVersion) { - setParameter(API_VERSION, apiVersion); - } - - /** - * Get the authorization key. - * - * @return the authorization key - */ - public String getAuthToken() { - return (String) getParameter(AUTH_TOKEN); - } - - /** - * Set the {@value #AUTH_TOKEN_LENGTH} character authorization key used to authenticate the API request. - * - * @param authToken the authorization key to set. A null value will remove this parameter - */ - public void setAuthToken(String authToken) { - if (authToken != null && authToken.length() != AUTH_TOKEN_LENGTH) { - throw new IllegalArgumentException(authToken + " is not " + AUTH_TOKEN_LENGTH + " characters long."); - } - setParameter(AUTH_TOKEN, authToken); - } - - /** - * Verifies that AuthToken has been set for this request. Will throw an - * {@link IllegalStateException} if not. - */ - public void verifyAuthTokenSet() { - if (getAuthToken() == null) { - throw new IllegalStateException("AuthToken must be set before this value can be set."); - } - } - - /** - * Get the campaign keyword - * - * @return the campaign keyword - */ - public String getCampaignKeyword() { - return (String) getParameter(CAMPAIGN_KEYWORD); - } - - /** - * Set the Campaign Keyword (see - * Tracking Campaigns). - * Used to populate the Referrers > Campaigns report (clicking on a - * campaign loads all keywords for this campaign). Note: this parameter - * will only be used for the first pageview of a visit. - * - * @param campaignKeyword the campaign keyword to set. A null value will remove this parameter - */ - public void setCampaignKeyword(String campaignKeyword) { - setParameter(CAMPAIGN_KEYWORD, campaignKeyword); - } - - /** - * Get the campaign name - * - * @return the campaign name - */ - public String getCampaignName() { - return (String) getParameter(CAMPAIGN_NAME); - } - - /** - * Set the Campaign Name (see - * Tracking Campaigns). - * Used to populate the Referrers > Campaigns report. Note: this parameter - * will only be used for the first pageview of a visit. - * - * @param campaignName the campaign name to set. A null value will remove this parameter - */ - public void setCampaignName(String campaignName) { - setParameter(CAMPAIGN_NAME, campaignName); - } - - /** - * Get the charset of the page being tracked - * - * @return the charset - */ - public Charset getCharacterSet() { - return (Charset) getParameter(CHARACTER_SET); - } - - /** - * The charset of the page being tracked. Specify the charset if the data - * you send to Piwik is encoded in a different character set than the default - * utf-8. - * - * @param characterSet the charset to set. A null value will remove this parameter - */ - public void setCharacterSet(Charset characterSet) { - setParameter(CHARACTER_SET, characterSet); - } - - /** - * Get the name of the interaction with the content - * - * @return the name of the interaction - */ - public String getContentInteraction() { - return (String) getParameter(CONTENT_INTERACTION); - } - - /** - * Set the name of the interaction with the content. For instance a 'click'. - * - * @param contentInteraction the name of the interaction to set. A null value will remove this parameter - */ - public void setContentInteraction(String contentInteraction) { - setParameter(CONTENT_INTERACTION, contentInteraction); - } - - /** - * Get the name of the content - * - * @return the name - */ - public String getContentName() { - return (String) getParameter(CONTENT_NAME); - } - - /** - * Set the name of the content. For instance 'Ad Foo Bar'. - * - * @param contentName the name to set. A null value will remove this parameter - */ - public void setContentName(String contentName) { - setParameter(CONTENT_NAME, contentName); - } - - /** - * Get the content piece. - * - * @return the content piece. - */ - public String getContentPiece() { - return (String) getParameter(CONTENT_PIECE); - } - - /** - * Set the actual content piece. For instance the path to an image, video, audio, any text. - * - * @param contentPiece the content piece to set. A null value will remove this parameter - */ - public void setContentPiece(String contentPiece) { - setParameter(CONTENT_PIECE, contentPiece); - } - - /** - * Get the content target - * - * @return the target - * @throws TypeConstraintException if the stored Content Target is a string - */ - public URL getContentTarget() { - return returnAsUrl(CONTENT_TARGET, "Content Target", "getContentTargetAsString"); - } - - /** - * Get the content target - * - * @return the target - * @throws TypeConstraintException if the stored Content Target is a URL - */ - public String getContentTargetAsString() { - return returnAsString(CONTENT_TARGET, "Content Target", "getContentTarget"); - } - - /** - * Set the target of the content. For instance the URL of a landing page. - * - * @param contentTarget the target to set. A null value will remove this parameter - */ - public void setContentTarget(URL contentTarget) { - setParameter(CONTENT_TARGET, contentTarget); - } - - /** - * Set the target of the content. For instance the URL of a landing page. - * - * @param contentTarget the target to set. A null value will remove this parameter - */ - public void setContentTargetWithString(String contentTarget) { - setParameter(CONTENT_TARGET, contentTarget); - } - - /** - * Get the current hour. - * - * @return the current hour - */ - public Integer getCurrentHour() { - return (Integer) getParameter(CURRENT_HOUR); - } - - /** - * Set the current hour (local time). - * - * @param currentHour the hour to set. A null value will remove this parameter - */ - public void setCurrentHour(Integer currentHour) { - setParameter(CURRENT_HOUR, currentHour); - } - - /** - * Get the current minute. - * - * @return the current minute - */ - public Integer getCurrentMinute() { - return (Integer) getParameter(CURRENT_MINUTE); - } - - /** - * Set the current minute (local time). - * - * @param currentMinute the minute to set. A null value will remove this parameter - */ - public void setCurrentMinute(Integer currentMinute) { - setParameter(CURRENT_MINUTE, currentMinute); - } - - /** - * Get the current second - * - * @return the current second - */ - public Integer getCurrentSecond() { - return (Integer) getParameter(CURRENT_SECOND); - } - - /** - * Set the current second (local time). - * - * @param currentSecond the second to set. A null value will remove this parameter - */ - public void setCurrentSecond(Integer currentSecond) { - setParameter(CURRENT_SECOND, currentSecond); - } - - /** - * Gets the list of objects currently stored at the specified custom tracking - * parameter. An empty list will be returned if there are no objects set at - * that key. - * - * @param key the key of the parameter whose list of objects to get. Cannot be null - * @return the list of objects currently stored at the specified key - */ - public List getCustomTrackingParameter(String key) { - if (key == null) { - throw new NullPointerException("Key cannot be null."); - } - List l = customTrackingParameters.get(key); - if (l == null) { - return new ArrayList(0); - } - return new ArrayList(l); - } - - /** - * Set a custom tracking parameter whose toString() value will be sent to - * the Piwik server. These parameters are stored separately from named Piwik - * parameters, meaning it is not possible to overwrite or clear named Piwik - * parameters with this method. A custom parameter that has the same name - * as a named Piwik parameter will be sent in addition to that named parameter. - * - * @param key the parameter's key. Cannot be null - * @param value the parameter's value. Removes the parameter if null - */ - public void setCustomTrackingParameter(String key, Object value) { - if (key == null) { - throw new NullPointerException("Key cannot be null."); - } - if (value == null) { - customTrackingParameters.remove(key); - } else { - List l = new ArrayList(); - l.add(value); - customTrackingParameters.put(key, l); - } - } - - /** - * Add a custom tracking parameter to the specified key. This allows users - * to have multiple parameters with the same name and different values, - * commonly used during situations where list parameters are needed - * - * @param key the parameter's key. Cannot be null - * @param value the parameter's value. Cannot be null - */ - public void addCustomTrackingParameter(String key, Object value) { - if (key == null) { - throw new NullPointerException("Key cannot be null."); - } - if (value == null) { - throw new NullPointerException("Cannot add a null custom tracking parameter."); - } else { - List l = customTrackingParameters.get(key); - if (l == null) { - l = new ArrayList(); - customTrackingParameters.put(key, l); - } - l.add(value); - } - } - - /** - * Removes all custom tracking parameters - */ - public void clearCustomTrackingParameter() { - customTrackingParameters.clear(); - } - - /** - * Get the resolution of the device - * - * @return the resolution - */ - public String getDeviceResolution() { - return (String) getParameter(DEVICE_RESOLUTION); - } - - /** - * Set the resolution of the device the visitor is using, eg 1280x1024. - * - * @param deviceResolution the resolution to set. A null value will remove this parameter - */ - public void setDeviceResolution(String deviceResolution) { - setParameter(DEVICE_RESOLUTION, deviceResolution); - } - - /** - * Get the url of a file the user had downloaded - * - * @return the url - * @throws TypeConstraintException if the stored Download URL is a String - */ - public URL getDownloadUrl() { - return returnAsUrl(DOWNLOAD_URL, "Download URL", "getDownloadUrlAsString"); - } - - /** - * Get the url of a file the user had downloaded - * - * @return the url - * @throws TypeConstraintException if the stored Download URL is a URL - */ - public String getDownloadUrlAsString() { - return returnAsString(DOWNLOAD_URL, "Download URL", "getDownloadUrl"); - } - - /** - * Set the url of a file the user has downloaded. Used for tracking downloads. - * We recommend to also set the url parameter to this same value. - * - * @param downloadUrl the url to set. A null value will remove this parameter - */ - public void setDownloadUrl(URL downloadUrl) { - setParameter(DOWNLOAD_URL, downloadUrl); - } - - /** - * Set the url of a file the user has downloaded. Used for tracking downloads. - * We recommend to also set the url parameter to this same value. - * - * @param downloadUrl the url to set. A null value will remove this parameter - */ - public void setDownloadUrlWithString(String downloadUrl) { - setParameter(DOWNLOAD_URL, downloadUrl); - } - - /** - * Sets idgoal=0 in the request to track an ecommerce interaction: - * cart update or an ecommerce order. - */ - public void enableEcommerce() { - setGoalId(0); - } - - /** - * Verifies that Ecommerce has been enabled for the request. Will throw an - * {@link IllegalStateException} if not. - */ - public void verifyEcommerceEnabled() { - if (getGoalId() == null || getGoalId() != 0) { - throw new IllegalStateException("GoalId must be \"0\". Try calling enableEcommerce first before calling this method."); - } - } - - /** - * Verifies that Ecommerce has been enabled and that Ecommerce Id and - * Ecommerce Revenue have been set for the request. Will throw an - * {@link IllegalStateException} if not. - */ - public void verifyEcommerceState() { - verifyEcommerceEnabled(); - if (getEcommerceId() == null) { - throw new IllegalStateException("EcommerceId must be set before this value can be set."); - } - if (getEcommerceRevenue() == null) { - throw new IllegalStateException("EcommerceRevenue must be set before this value can be set."); - } - } - - /** - * Get the discount offered. - * - * @return the discount - */ - public Double getEcommerceDiscount() { - return (Double) getParameter(ECOMMERCE_DISCOUNT); - } - - /** - * Set the discount offered. Ecommerce must be enabled, and EcommerceId and - * EcommerceRevenue must first be set. - * - * @param discount the discount to set. A null value will remove this parameter - */ - public void setEcommerceDiscount(Double discount) { - if (discount != null) { - verifyEcommerceState(); - } - setParameter(ECOMMERCE_DISCOUNT, discount); - } - - /** - * Get the id of this order. - * - * @return the id - */ - public String getEcommerceId() { - return (String) getParameter(ECOMMERCE_ID); - } - - /** - * Set the unique string identifier for the ecommerce order (required when - * tracking an ecommerce order). Ecommerce must be enabled. - * - * @param id the id to set. A null value will remove this parameter - */ - public void setEcommerceId(String id) { - if (id != null) { - verifyEcommerceEnabled(); - } - setParameter(ECOMMERCE_ID, id); - } - - /** - * Get the {@link EcommerceItem} at the specified index - * - * @param index the index of the {@link EcommerceItem} to return - * @return the {@link EcommerceItem} at the specified index - */ - public EcommerceItem getEcommerceItem(int index) { - return (EcommerceItem) getFromJsonArray(ECOMMERCE_ITEMS, index); - } - - /** - * Add an {@link EcommerceItem} to this order. Ecommerce must be enabled, - * and EcommerceId and EcommerceRevenue must first be set. - * - * @param item the {@link EcommerceItem} to add. Cannot be null - */ - public void addEcommerceItem(EcommerceItem item) { - if (item != null) { - verifyEcommerceState(); - } - addToJsonArray(ECOMMERCE_ITEMS, item); - } - - /** - * Clears all {@link EcommerceItem} from this order. - */ - public void clearEcommerceItems() { - removeJsonArray(ECOMMERCE_ITEMS); - } - - /** - * Get the timestamp of the customer's last ecommerce order - * - * @return the timestamp - */ - public Long getEcommerceLastOrderTimestamp() { - return (Long) getParameter(ECOMMERCE_LAST_ORDER_TIMESTAMP); - } - - /** - * Set the UNUX timestamp of this customer's last ecommerce order. This value - * is used to process the "Days since last order" report. Ecommerce must be - * enabled, and EcommerceId and EcommerceRevenue must first be set. - * - * @param timestamp the timestamp to set. A null value will remove this parameter - */ - public void setEcommerceLastOrderTimestamp(Long timestamp) { - if (timestamp != null) { - verifyEcommerceState(); - } - setParameter(ECOMMERCE_LAST_ORDER_TIMESTAMP, timestamp); - } - - /** - * Get the grand total of the ecommerce order. - * - * @return the grand total - */ - public Double getEcommerceRevenue() { - return (Double) getParameter(ECOMMERCE_REVENUE); - } - - /** - * Set the grand total of the ecommerce order (required when tracking an - * ecommerce order). Ecommerce must be enabled. - * - * @param revenue the grand total to set. A null value will remove this parameter - */ - public void setEcommerceRevenue(Double revenue) { - if (revenue != null) { - verifyEcommerceEnabled(); - } - setParameter(ECOMMERCE_REVENUE, revenue); - } - - /** - * Get the shipping cost of the ecommerce order. - * - * @return the shipping cost - */ - public Double getEcommerceShippingCost() { - return (Double) getParameter(ECOMMERCE_SHIPPING_COST); - } - - /** - * Set the shipping cost of the ecommerce order. Ecommerce must be enabled, - * and EcommerceId and EcommerceRevenue must first be set. - * - * @param shippingCost the shipping cost to set. A null value will remove this parameter - */ - public void setEcommerceShippingCost(Double shippingCost) { - if (shippingCost != null) { - verifyEcommerceState(); - } - setParameter(ECOMMERCE_SHIPPING_COST, shippingCost); - } - - /** - * Get the subtotal of the ecommerce order; excludes shipping. - * - * @return the subtotal - */ - public Double getEcommerceSubtotal() { - return (Double) getParameter(ECOMMERCE_SUBTOTAL); - } - - /** - * Set the subtotal of the ecommerce order; excludes shipping. Ecommerce - * must be enabled and EcommerceId and EcommerceRevenue must first be set. - * - * @param subtotal the subtotal to set. A null value will remove this parameter - */ - public void setEcommerceSubtotal(Double subtotal) { - if (subtotal != null) { - verifyEcommerceState(); - } - setParameter(ECOMMERCE_SUBTOTAL, subtotal); - } - - /** - * Get the tax amount of the ecommerce order. - * - * @return the tax amount - */ - public Double getEcommerceTax() { - return (Double) getParameter(ECOMMERCE_TAX); - } - - /** - * Set the tax amount of the ecommerce order. Ecommerce must be enabled, and - * EcommerceId and EcommerceRevenue must first be set. - * - * @param tax the tax amount to set. A null value will remove this parameter - */ - public void setEcommerceTax(Double tax) { - if (tax != null) { - verifyEcommerceState(); - } - setParameter(ECOMMERCE_TAX, tax); - } - - /** - * Get the event action. - * - * @return the event action - */ - public String getEventAction() { - return getNonEmptyStringParameter(EVENT_ACTION); - } - - /** - * Set the event action. Must not be empty. (eg. Play, Pause, Duration, - * Add Playlist, Downloaded, Clicked...). - * - * @param eventAction the event action to set. A null value will remove this parameter - */ - public void setEventAction(String eventAction) { - setNonEmptyStringParameter(EVENT_ACTION, eventAction); - } - - /** - * Get the event category. - * - * @return the event category - */ - public String getEventCategory() { - return getNonEmptyStringParameter(EVENT_CATEGORY); - } - - /** - * Set the event category. Must not be empty. (eg. Videos, Music, Games...). - * - * @param eventCategory the event category to set. A null value will remove this parameter - */ - public void setEventCategory(String eventCategory) { - setNonEmptyStringParameter(EVENT_CATEGORY, eventCategory); - } - - /** - * Get the event name. - * - * @return the event name - */ - public String getEventName() { - return (String) getParameter(EVENT_NAME); - } - - /** - * Set the event name. (eg. a Movie name, or Song name, or File name...). - * - * @param eventName the event name to set. A null value will remove this parameter - */ - public void setEventName(String eventName) { - setParameter(EVENT_NAME, eventName); - } - - /** - * Get the event value. - * - * @return the event value - */ - public Number getEventValue() { - return (Number) getParameter(EVENT_VALUE); - } - - /** - * Set the event value. Must be a float or integer value (numeric), not a string. - * - * @param eventValue the event value to set. A null value will remove this parameter - */ - public void setEventValue(Number eventValue) { - setParameter(EVENT_VALUE, eventValue); - } - - /** - * Get the goal id - * - * @return the goal id - */ - public Integer getGoalId() { - return (Integer) getParameter(GOAL_ID); - } - - /** - * Set the goal id. If specified, the tracking request will trigger a - * conversion for the goal of the website being tracked with this id. - * - * @param goalId the goal id to set. A null value will remove this parameter - */ - public void setGoalId(Integer goalId) { - setParameter(GOAL_ID, goalId); - } - - /** - * Get the goal revenue. - * - * @return the goal revenue - */ - public Double getGoalRevenue() { - return (Double) getParameter(GOAL_REVENUE); - } - - /** - * Set a monetary value that was generated as revenue by this goal conversion. - * Only used if idgoal is specified in the request. - * - * @param goalRevenue the goal revenue to set. A null value will remove this parameter - */ - public void setGoalRevenue(Double goalRevenue) { - if (goalRevenue != null && getGoalId() == null) { - throw new IllegalStateException("GoalId must be set before GoalRevenue can be set."); - } - setParameter(GOAL_REVENUE, goalRevenue); - } - - /** - * Get the Accept-Language HTTP header - * - * @return the Accept-Language HTTP header - */ - public String getHeaderAcceptLanguage() { - return (String) getParameter(HEADER_ACCEPT_LANGUAGE); - } - - /** - * Set an override value for the Accept-Language HTTP header - * field. This value is used to detect the visitor's country if - * GeoIP is not enabled. - * - * @param acceptLangage the Accept-Language HTTP header to set. A null value will remove this parameter - */ - public void setHeaderAcceptLanguage(String acceptLangage) { - setParameter(HEADER_ACCEPT_LANGUAGE, acceptLangage); - } - - /** - * Get the User-Agent HTTP header - * - * @return the User-Agent HTTP header - */ - public String getHeaderUserAgent() { - return (String) getParameter(HEADER_USER_AGENT); - } - - /** - * Set an override value for the User-Agent HTTP header field. - * The user agent is used to detect the operating system and browser used. - * - * @param userAgent the User-Agent HTTP header tos et - */ - public void setHeaderUserAgent(String userAgent) { - setParameter(HEADER_USER_AGENT, userAgent); - } - - /** - * Get if this request will force a new visit. - * - * @return true if this request will force a new visit - */ - public Boolean getNewVisit() { - return getBooleanParameter(NEW_VISIT); - } - - /** - * If set to true, will force a new visit to be created for this action. - * - * @param newVisit if this request will force a new visit - */ - public void setNewVisit(Boolean newVisit) { - setBooleanParameter(NEW_VISIT, newVisit); - } - - /** - * Get the outlink url - * - * @return the outlink url - * @throws TypeConstraintException if the stored Outlink URL is a String - */ - public URL getOutlinkUrl() { - return returnAsUrl(OUTLINK_URL, "Outlink URL", "getOutlinkUrlAsString"); - } - - /** - * Get the outlink url - * - * @return the outlink url - * @throws TypeConstraintException if the stored Outlink URL is a URL - */ - public String getOutlinkUrlAsString() { - return returnAsString(OUTLINK_URL, "Outlink URL", "getOutlinkUrl"); - } - - /** - * Set an external URL the user has opened. Used for tracking outlink clicks. - * We recommend to also set the url parameter to this same value. - * - * @param outlinkUrl the outlink url to set. A null value will remove this parameter - */ - public void setOutlinkUrl(URL outlinkUrl) { - setParameter(OUTLINK_URL, outlinkUrl); - } - - /** - * Set an external URL the user has opened. Used for tracking outlink clicks. - * We recommend to also set the url parameter to this same value. - * - * @param outlinkUrl the outlink url to set. A null value will remove this parameter - */ - public void setOutlinkUrlWithString(String outlinkUrl) { - setParameter(OUTLINK_URL, outlinkUrl); - } - - /** - * Get the page custom variable at the specified key. - * - * @param key the key of the variable to get - * @return the variable at the specified key, null if key is not present - * @deprecated Use the {@link #getPageCustomVariable(int)} method instead. - */ - @Deprecated - public String getPageCustomVariable(String key) { - return getCustomVariable(PAGE_CUSTOM_VARIABLE, key); - } - - /** - * Get the page custom variable at the specified index. - * - * @param index the index of the variable to get. Must be greater than 0 - * @return the variable at the specified key, null if nothing at this index - */ - public CustomVariable getPageCustomVariable(int index) { - return getCustomVariable(PAGE_CUSTOM_VARIABLE, index); - } - - /** - * Set a page custom variable with the specified key and value at the first available index. - * All page custom variables with this key will be overwritten or deleted - * - * @param key the key of the variable to set - * @param value the value of the variable to set at the specified key. A null value will remove this custom variable - * @deprecated Use the {@link #setPageCustomVariable(CustomVariable, int)} method instead. - */ - @Deprecated - public void setPageCustomVariable(String key, String value) { - if (value == null) { - removeCustomVariable(PAGE_CUSTOM_VARIABLE, key); - } else { - setCustomVariable(PAGE_CUSTOM_VARIABLE, new CustomVariable(key, value), null); - } - } - - /** - * Set a page custom variable at the specified index. - * - * @param customVariable the CustomVariable to set. A null value will remove the CustomVariable at the specified index - * @param index the index of he CustomVariable to set - */ - public void setPageCustomVariable(CustomVariable customVariable, int index) { - setCustomVariable(PAGE_CUSTOM_VARIABLE, customVariable, index); - } - - /** - * Check if the visitor has the Director plugin. - * - * @return true if visitor has the Director plugin - */ - public Boolean getPluginDirector() { - return getBooleanParameter(PLUGIN_DIRECTOR); - } - - /** - * Set if the visitor has the Director plugin. - * - * @param director true if the visitor has the Director plugin - */ - public void setPluginDirector(Boolean director) { - setBooleanParameter(PLUGIN_DIRECTOR, director); - } - - /** - * Check if the visitor has the Flash plugin. - * - * @return true if the visitor has the Flash plugin - */ - public Boolean getPluginFlash() { - return getBooleanParameter(PLUGIN_FLASH); - } - - /** - * Set if the visitor has the Flash plugin. - * - * @param flash true if the visitor has the Flash plugin - */ - public void setPluginFlash(Boolean flash) { - setBooleanParameter(PLUGIN_FLASH, flash); - } - - /** - * Check if the visitor has the Gears plugin. - * - * @return true if the visitor has the Gears plugin - */ - public Boolean getPluginGears() { - return getBooleanParameter(PLUGIN_GEARS); - } - - /** - * Set if the visitor has the Gears plugin. - * - * @param gears true if the visitor has the Gears plugin - */ - public void setPluginGears(Boolean gears) { - setBooleanParameter(PLUGIN_GEARS, gears); - } - - /** - * Check if the visitor has the Java plugin. - * - * @return true if the visitor has the Java plugin - */ - public Boolean getPluginJava() { - return getBooleanParameter(PLUGIN_JAVA); - } - - /** - * Set if the visitor has the Java plugin. - * - * @param java true if the visitor has the Java plugin - */ - public void setPluginJava(Boolean java) { - setBooleanParameter(PLUGIN_JAVA, java); - } - - /** - * Check if the visitor has the PDF plugin. - * - * @return true if the visitor has the PDF plugin - */ - public Boolean getPluginPDF() { - return getBooleanParameter(PLUGIN_PDF); - } - - /** - * Set if the visitor has the PDF plugin. - * - * @param pdf true if the visitor has the PDF plugin - */ - public void setPluginPDF(Boolean pdf) { - setBooleanParameter(PLUGIN_PDF, pdf); - } - - /** - * Check if the visitor has the Quicktime plugin. - * - * @return true if the visitor has the Quicktime plugin - */ - public Boolean getPluginQuicktime() { - return getBooleanParameter(PLUGIN_QUICKTIME); - } - - /** - * Set if the visitor has the Quicktime plugin. - * - * @param quicktime true if the visitor has the Quicktime plugin - */ - public void setPluginQuicktime(Boolean quicktime) { - setBooleanParameter(PLUGIN_QUICKTIME, quicktime); - } - - /** - * Check if the visitor has the RealPlayer plugin. - * - * @return true if the visitor has the RealPlayer plugin - */ - public Boolean getPluginRealPlayer() { - return getBooleanParameter(PLUGIN_REAL_PLAYER); - } - - /** - * Set if the visitor has the RealPlayer plugin. - * - * @param realPlayer true if the visitor has the RealPlayer plugin - */ - public void setPluginRealPlayer(Boolean realPlayer) { - setBooleanParameter(PLUGIN_REAL_PLAYER, realPlayer); - } - - /** - * Check if the visitor has the Silverlight plugin. - * - * @return true if the visitor has the Silverlight plugin - */ - public Boolean getPluginSilverlight() { - return getBooleanParameter(PLUGIN_SILVERLIGHT); - } - - /** - * Set if the visitor has the Silverlight plugin. - * - * @param silverlight true if the visitor has the Silverlight plugin - */ - public void setPluginSilverlight(Boolean silverlight) { - setBooleanParameter(PLUGIN_SILVERLIGHT, silverlight); - } - - /** - * Check if the visitor has the Windows Media plugin. - * - * @return true if the visitor has the Windows Media plugin - */ - public Boolean getPluginWindowsMedia() { - return getBooleanParameter(PLUGIN_WINDOWS_MEDIA); - } - - /** - * Set if the visitor has the Windows Media plugin. - * - * @param windowsMedia true if the visitor has the Windows Media plugin - */ - public void setPluginWindowsMedia(Boolean windowsMedia) { - setBooleanParameter(PLUGIN_WINDOWS_MEDIA, windowsMedia); - } - - /** - * Get the random value for this request - * - * @return the random value - */ - public String getRandomValue() { - return (String) getParameter(RANDOM_VALUE); - } - - /** - * Set a random value that is generated before each request. Using it helps - * avoid the tracking request being cached by the browser or a proxy. - * - * @param randomValue the random value to set. A null value will remove this parameter - */ - public void setRandomValue(String randomValue) { - setParameter(RANDOM_VALUE, randomValue); - } - - /** - * Get the referrer url - * - * @return the referrer url - * @throws TypeConstraintException if the stored Referrer URL is a String - */ - public URL getReferrerUrl() { - return returnAsUrl(REFERRER_URL, "Referrer URL", "getReferrerUrlAsString"); - } - - /** - * Get the referrer url - * - * @return the referrer url - * @throws TypeConstraintException if the stored Referrer URL is a URL - */ - public String getReferrerUrlAsString() { - return returnAsString(REFERRER_URL, "Referrer URL", "getReferrerUrl"); - } - - /** - * Set the full HTTP Referrer URL. This value is used to determine how someone - * got to your website (ie, through a website, search engine or campaign). - * - * @param referrerUrl the referrer url to set. A null value will remove this parameter - */ - public void setReferrerUrl(URL referrerUrl) { - setParameter(REFERRER_URL, referrerUrl); - } - - /** - * Set the full HTTP Referrer URL. This value is used to determine how someone - * got to your website (ie, through a website, search engine or campaign). - * - * @param referrerUrl the referrer url to set. A null value will remove this parameter - */ - public void setReferrerUrlWithString(String referrerUrl) { - setParameter(REFERRER_URL, referrerUrl); - } - - /** - * Get the datetime of the request - * - * @return the datetime of the request - */ - public PiwikDate getRequestDatetime() { - return (PiwikDate) getParameter(REQUEST_DATETIME); - } - - /** - * Set the datetime of the request (normally the current time is used). - * This can be used to record visits and page views in the past. The datetime - * must be sent in UTC timezone. Note: if you record data in the past, you will - * need to force Piwik to re-process - * reports for the past dates. If you set the Request Datetime to a datetime - * older than four hours then Auth Token must be set. If you set - * Request Datetime with a datetime in the last four hours then you - * don't need to pass Auth Token. - * - * @param datetime the datetime of the request to set. A null value will remove this parameter - */ - public void setRequestDatetime(PiwikDate datetime) { - if (datetime != null && new Date().getTime() - datetime.getTime() > REQUEST_DATETIME_AUTH_LIMIT && getAuthToken() == null) { - throw new IllegalStateException("Because you are trying to set RequestDatetime for a time greater than 4 hours ago, AuthToken must be set first."); - } - setParameter(REQUEST_DATETIME, datetime); - } - - /** - * Get if this request will be tracked. - * - * @return true if request will be tracked - */ - public Boolean getRequired() { - return getBooleanParameter(REQUIRED); - } - - /** - * Set if this request will be tracked by the Piwik server. - * - * @param required true if request will be tracked - */ - public void setRequired(Boolean required) { - setBooleanParameter(REQUIRED, required); - } - - /** - * Get if the response will be an image. - * - * @return true if the response will be an an image - */ - public Boolean getResponseAsImage() { - return getBooleanParameter(RESPONSE_AS_IMAGE); - } - - /** - * Set if the response will be an image. If set to false, Piwik will respond - * with a HTTP 204 response code instead of a GIF image. This improves performance - * and can fix errors if images are not allowed to be obtained directly - * (eg Chrome Apps). Available since Piwik 2.10.0. - * - * @param responseAsImage true if the response will be an image - */ - public void setResponseAsImage(Boolean responseAsImage) { - setBooleanParameter(RESPONSE_AS_IMAGE, responseAsImage); - } - - /** - * Get the search category - * - * @return the search category - */ - public String getSearchCategory() { - return (String) getParameter(SEARCH_CATEGORY); - } - - /** - * Specify a search category with this parameter. SearchQuery must first be - * set. - * - * @param searchCategory the search category to set. A null value will remove this parameter - */ - public void setSearchCategory(String searchCategory) { - if (searchCategory != null && getSearchQuery() == null) { - throw new IllegalStateException("SearchQuery must be set before SearchCategory can be set."); - } - setParameter(SEARCH_CATEGORY, searchCategory); - } - - /** - * Get the search query. - * - * @return the search query - */ - public String getSearchQuery() { - return (String) getParameter(SEARCH_QUERY); - } - - /** - * Set the search query. When specified, the request will not be tracked as - * a normal pageview but will instead be tracked as a Site Search request. - * - * @param searchQuery the search query to set. A null value will remove this parameter - */ - public void setSearchQuery(String searchQuery) { - setParameter(SEARCH_QUERY, searchQuery); - } - - /** - * Get the search results count. - * - * @return the search results count - */ - public Long getSearchResultsCount() { - return (Long) getParameter(SEARCH_RESULTS_COUNT); - } - - /** - * We recommend to set the - * search count to the number of search results displayed on the results page. - * When keywords are tracked with {@code Search Results Count=0} they will appear in - * the "No Result Search Keyword" report. SearchQuery must first be set. - * - * @param searchResultsCount the search results count to set. A null value will remove this parameter - */ - public void setSearchResultsCount(Long searchResultsCount) { - if (searchResultsCount != null && getSearchQuery() == null) { - throw new IllegalStateException("SearchQuery must be set before SearchResultsCount can be set."); - } - setParameter(SEARCH_RESULTS_COUNT, searchResultsCount); - } - - /** - * Get the id of the website we're tracking. - * - * @return the id of the website - */ - public Integer getSiteId() { - return (Integer) getParameter(SITE_ID); - } - - /** - * Set the ID of the website we're tracking a visit/action for. - * - * @param siteId the id of the website to set. A null value will remove this parameter - */ - public void setSiteId(Integer siteId) { - setParameter(SITE_ID, siteId); - } - - /** - * Set if bot requests should be tracked - * - * @return true if bot requests should be tracked - */ - public Boolean getTrackBotRequests() { - return getBooleanParameter(TRACK_BOT_REQUESTS); - } - - /** - * By default Piwik does not track bots. If you use the Tracking Java API, - * you may be interested in tracking bot requests. To enable Bot Tracking in - * Piwik, set Track Bot Requests to true. - * - * @param trackBotRequests true if bot requests should be tracked - */ - public void setTrackBotRequests(Boolean trackBotRequests) { - setBooleanParameter(TRACK_BOT_REQUESTS, trackBotRequests); - } - - /** - * Get the visit custom variable at the specified key. - * - * @param key the key of the variable to get - * @return the variable at the specified key, null if key is not present - * @deprecated Use the {@link #getVisitCustomVariable(int)} method instead. - */ - @Deprecated - public String getUserCustomVariable(String key) { - return getCustomVariable(VISIT_CUSTOM_VARIABLE, key); - } - - /** - * Get the visit custom variable at the specified index. - * - * @param index the index of the variable to get - * @return the variable at the specified index, null if nothing at this index - */ - public CustomVariable getVisitCustomVariable(int index) { - return getCustomVariable(VISIT_CUSTOM_VARIABLE, index); - } - - /** - * Set a visit custom variable with the specified key and value at the first available index. - * All visit custom variables with this key will be overwritten or deleted - * - * @param key the key of the variable to set - * @param value the value of the variable to set at the specified key. A null value will remove this parameter - * @deprecated Use the {@link #setVisitCustomVariable(CustomVariable, int)} method instead. - */ - @Deprecated - public void setUserCustomVariable(String key, String value) { - if (value == null) { - removeCustomVariable(VISIT_CUSTOM_VARIABLE, key); - } else { - setCustomVariable(VISIT_CUSTOM_VARIABLE, new CustomVariable(key, value), null); - } - } - - /** - * Set a user custom variable at the specified key. - * - * @param customVariable the CustomVariable to set. A null value will remove the custom variable at the specified index - * @param index the index to set the customVariable at. - */ - public void setVisitCustomVariable(CustomVariable customVariable, int index) { - setCustomVariable(VISIT_CUSTOM_VARIABLE, customVariable, index); - } - - /** - * Get the user id for this request. - * - * @return the user id - */ - public String getUserId() { - return (String) getParameter(USER_ID); - } - - /** - * Set the user id for this request. - * User id is any non empty unique string identifying the user (such as an email - * address or a username). To access this value, users must be logged-in in your - * system so you can fetch this user id from your system, and pass it to Piwik. - * The user id appears in the visitor log, the Visitor profile, and you can - * Segment - * reports for one or several user ids. When specified, the user id will be - * "enforced". This means that if there is no recent visit with this user id, - * a new one will be created. If a visit is found in the last 30 minutes with - * your specified user id, then the new action will be recorded to this existing visit. - * - * @param userId the user id to set. A null value will remove this parameter - */ - public void setUserId(String userId) { - setNonEmptyStringParameter(USER_ID, userId); - } - - /** - * Get the visitor's city. - * - * @return the visitor's city - */ - public String getVisitorCity() { - return (String) getParameter(VISITOR_CITY); - } - - /** - * Set an override value for the city. The name of the city the visitor is - * located in, eg, Tokyo. AuthToken must first be set. - * - * @param city the visitor's city to set. A null value will remove this parameter - */ - public void setVisitorCity(String city) { - if (city != null) { - verifyAuthTokenSet(); - } - setParameter(VISITOR_CITY, city); - } - - /** - * Get the visitor's country. - * - * @return the visitor's country - */ - public PiwikLocale getVisitorCountry() { - return (PiwikLocale) getParameter(VISITOR_COUNTRY); - } - - /** - * Set an override value for the country. AuthToken must first be set. - * - * @param country the visitor's country to set. A null value will remove this parameter - */ - public void setVisitorCountry(PiwikLocale country) { - if (country != null) { - verifyAuthTokenSet(); - } - setParameter(VISITOR_COUNTRY, country); - } - - /** - * Get the visitor's custom id. - * - * @return the visitor's custom id - */ - public String getVisitorCustomId() { - return (String) getParameter(VISITOR_CUSTOM_ID); - } - - /** - * Set a custom visitor ID for this request. You must set this value to exactly - * a {@value #ID_LENGTH} character hexadecimal string (containing only characters 01234567890abcdefABCDEF). - * We recommended to set the UserId rather than the VisitorCustomId. - * - * @param visitorCustomId the visitor's custom id to set. A null value will remove this parameter - */ - public void setVisitorCustomId(String visitorCustomId) { - if (visitorCustomId != null) { - if (visitorCustomId.length() != ID_LENGTH) { - throw new IllegalArgumentException(visitorCustomId + " is not " + ID_LENGTH + " characters long."); - } - // Verify visitorID is a 16 character hexadecimal string - else if (!visitorCustomId.matches("[0-9A-Fa-f]+")) { - throw new IllegalArgumentException(visitorCustomId + " is not a hexadecimal string."); - } - } - setParameter(VISITOR_CUSTOM_ID, visitorCustomId); - } - - /** - * Get the timestamp of the visitor's first visit. - * - * @return the timestamp of the visitor's first visit - */ - public Long getVisitorFirstVisitTimestamp() { - return (Long) getParameter(VISITOR_FIRST_VISIT_TIMESTAMP); - } - - /** - * Set the UNIX timestamp of this visitor's first visit. This could be set - * to the date where the user first started using your software/app, or when - * he/she created an account. This parameter is used to populate the - * Goals > Days to Conversion report. - * - * @param timestamp the timestamp of the visitor's first visit to set. A null value will remove this parameter - */ - public void setVisitorFirstVisitTimestamp(Long timestamp) { - setParameter(VISITOR_FIRST_VISIT_TIMESTAMP, timestamp); - } - - /** - * Get the visitor's id. - * - * @return the visitor's id - */ - public String getVisitorId() { - return (String) getParameter(VISITOR_ID); - } - - /** - * Set the unique visitor ID, must be a {@value #ID_LENGTH} characters hexadecimal string. - * Every unique visitor must be assigned a different ID and this ID must not - * change after it is assigned. If this value is not set Piwik will still - * track visits, but the unique visitors metric might be less accurate. - * - * @param visitorId the visitor id to set. A null value will remove this parameter - */ - public void setVisitorId(String visitorId) { - if (visitorId != null) { - if (visitorId.length() != ID_LENGTH) { - throw new IllegalArgumentException(visitorId + " is not " + ID_LENGTH + " characters long."); - } - // Verify visitorID is a 16 character hexadecimal string - else if (!visitorId.matches("[0-9A-Fa-f]+")) { - throw new IllegalArgumentException(visitorId + " is not a hexadecimal string."); - } - } - setParameter(VISITOR_ID, visitorId); - } - - /** - * Get the visitor's ip. - * - * @return the visitor's ip - */ - public String getVisitorIp() { - return (String) getParameter(VISITOR_IP); - } - - /** - * Set the override value for the visitor IP (both IPv4 and IPv6 notations - * supported). AuthToken must first be set. - * - * @param visitorIp the visitor's ip to set. A null value will remove this parameter - */ - public void setVisitorIp(String visitorIp) { - if (visitorIp != null) { - verifyAuthTokenSet(); - } - setParameter(VISITOR_IP, visitorIp); - } - - /** - * Get the visitor's latitude. - * - * @return the visitor's latitude - */ - public Double getVisitorLatitude() { - return (Double) getParameter(VISITOR_LATITUDE); - } - - /** - * Set an override value for the visitor's latitude, eg 22.456. AuthToken - * must first be set. - * - * @param latitude the visitor's latitude to set. A null value will remove this parameter - */ - public void setVisitorLatitude(Double latitude) { - if (latitude != null) { - verifyAuthTokenSet(); - } - setParameter(VISITOR_LATITUDE, latitude); - } - - /** - * Get the visitor's longitude. - * - * @return the visitor's longitude - */ - public Double getVisitorLongitude() { - return (Double) getParameter(VISITOR_LONGITUDE); - } - - /** - * Set an override value for the visitor's longitude, eg 22.456. AuthToken - * must first be set. - * - * @param longitude the visitor's longitude to set. A null value will remove this parameter - */ - public void setVisitorLongitude(Double longitude) { - if (longitude != null) { - verifyAuthTokenSet(); - } - setParameter(VISITOR_LONGITUDE, longitude); - } - - /** - * Get the timestamp of the visitor's previous visit. - * - * @return the timestamp of the visitor's previous visit - */ - public Long getVisitorPreviousVisitTimestamp() { - return (Long) getParameter(VISITOR_PREVIOUS_VISIT_TIMESTAMP); - } - - /** - * Set the UNIX timestamp of this visitor's previous visit. This parameter - * is used to populate the report - * Visitors > Engagement > Visits by days since last visit. - * - * @param timestamp the timestamp of the visitor's previous visit to set. A null value will remove this parameter - */ - public void setVisitorPreviousVisitTimestamp(Long timestamp) { - setParameter(VISITOR_PREVIOUS_VISIT_TIMESTAMP, timestamp); - } - - /** - * Get the visitor's region. - * - * @return the visitor's region - */ - public String getVisitorRegion() { - return (String) getParameter(VISITOR_REGION); - } - - /** - * Set an override value for the region. Should be set to the two letter - * region code as defined by - * MaxMind's GeoIP databases. - * See here - * for a list of them for every country (the region codes are located in the - * second column, to the left of the region name and to the right of the country - * code). - * - * @param region the visitor's region to set. A null value will remove this parameter - */ - public void setVisitorRegion(String region) { - if (region != null) { - verifyAuthTokenSet(); - } - setParameter(VISITOR_REGION, region); - } - - /** - * Get the count of visits for this visitor. - * - * @return the count of visits for this visitor - */ - public Integer getVisitorVisitCount() { - return (Integer) getParameter(VISITOR_VISIT_COUNT); - } - - /** - * Set the current count of visits for this visitor. To set this value correctly, - * it would be required to store the value for each visitor in your application - * (using sessions or persisting in a database). Then you would manually increment - * the counts by one on each new visit or "session", depending on how you choose - * to define a visit. This value is used to populate the report - * Visitors > Engagement > Visits by visit number. - * - * @param visitorVisitCount the count of visits for this visitor to set. A null value will remove this parameter - */ - public void setVisitorVisitCount(Integer visitorVisitCount) { - setParameter(VISITOR_VISIT_COUNT, visitorVisitCount); - } - - /** - * Get the query string represented by this object. - * - * @return the query string represented by this object - */ - - public String getQueryString() { - StringBuilder sb = new StringBuilder(); - for (Entry parameter : parameters.entrySet()) { - if (sb.length() > 0) { - sb.append("&"); - } - sb.append(parameter.getKey()); - sb.append("="); - sb.append(parameter.getValue().toString()); - } - for (Entry customTrackingParameter : customTrackingParameters.entrySet()) { - for (Object o : customTrackingParameter.getValue()) { - if (sb.length() > 0) { - sb.append("&"); - } - sb.append(customTrackingParameter.getKey()); - sb.append("="); - sb.append(o.toString()); - } - } - - return sb.toString(); - } - - /** - * Get the url encoded query string represented by this object. - * - * @return the url encoded query string represented by this object - */ - public String getUrlEncodedQueryString() { - StringBuilder sb = new StringBuilder(); - for (Entry parameter : parameters.entrySet()) { - if (sb.length() > 0) { - sb.append("&"); - } - try { - StringBuilder sb2 = new StringBuilder(); - sb2.append(parameter.getKey()); - sb2.append("="); - sb2.append(URLEncoder.encode(parameter.getValue().toString(), "UTF-8")); - sb.append(sb2); - } catch (UnsupportedEncodingException e) { - System.err.println(e.getMessage()); - } - } - for (Entry customTrackingParameter : customTrackingParameters.entrySet()) { - for (Object o : customTrackingParameter.getValue()) { - if (sb.length() > 0) { - sb.append("&"); - } - try { - StringBuilder sb2 = new StringBuilder(); - sb2.append(URLEncoder.encode(customTrackingParameter.getKey(), "UTF-8")); - sb2.append("="); - sb2.append(URLEncoder.encode(o.toString(), "UTF-8")); - sb.append(sb2); - } catch (UnsupportedEncodingException e) { - System.err.println(e.getMessage()); - } - } - } - - return sb.toString(); - } - - /** - * Get a random hexadecimal string of a specified length. - * - * @param length length of the string to produce - * @return a random string consisting only of hexadecimal characters - */ - public static String getRandomHexString(int length) { - byte[] bytes = new byte[length / 2]; - new Random().nextBytes(bytes); - return DatatypeConverter.printHexBinary(bytes); - } - - /** - * Get a stored parameter. - * - * @param key the parameter's key - * @return the stored parameter's value - */ - private Object getParameter(String key) { - return parameters.get(key); - } - - /** - * Set a stored parameter. - * - * @param key the parameter's key - * @param value the parameter's value. Removes the parameter if null - */ - private void setParameter(String key, Object value) { - if (value == null) { - parameters.remove(key); - } else { - parameters.put(key, value); - } - } - - /** - * Get a stored parameter that is a non-empty string. - * - * @param key the parameter's key - * @return the stored parameter's value - */ - private String getNonEmptyStringParameter(String key) { - return (String) parameters.get(key); - } - - /** - * Set a stored parameter and verify it is a non-empty string. - * - * @param key the parameter's key - * @param value the parameter's value. Cannot be the empty. Removes the parameter if null - * string - */ - private void setNonEmptyStringParameter(String key, String value) { - if (value == null) { - parameters.remove(key); - } else if (value.length() == 0) { - throw new IllegalArgumentException("Value cannot be empty."); - } else { - parameters.put(key, value); - } - } - - /** - * Get a stored parameter that is a boolean. - * - * @param key the parameter's key - * @return the stored parameter's value - */ - private Boolean getBooleanParameter(String key) { - Integer i = (Integer) parameters.get(key); - if (i == null) { - return null; - } - return i.equals(1); - } - - /** - * Set a stored parameter that is a boolean. This value will be stored as "1" - * for true and "0" for false. - * - * @param key the parameter's key - * @param value the parameter's value. Removes the parameter if null - */ - private void setBooleanParameter(String key, Boolean value) { - if (value == null) { - parameters.remove(key); - } else if (value) { - parameters.put(key, 1); - } else { - parameters.put(key, 0); - } - } - - /** - * Get a value that is stored in a json object at the specified parameter. - * - * @param parameter the parameter to retrieve the json object from - * @param key the key of the value. Cannot be null - * @return the value - */ - private CustomVariable getCustomVariable(String parameter, int index) { - CustomVariableList cvl = (CustomVariableList) parameters.get(parameter); - if (cvl == null) { - return null; - } - - return cvl.get(index); - } - - private String getCustomVariable(String parameter, String key) { - if (key == null) { - throw new NullPointerException("Key cannot be null."); - } - - CustomVariableList cvl = (CustomVariableList) parameters.get(parameter); - if (cvl == null) { - return null; - } - - return cvl.get(key); - } - - /** - * Store a value in a json object at the specified parameter. - * - * @param parameter the parameter to store the json object at - * @param key the key of the value. Cannot be null - * @param value the value. Removes the parameter if null - */ - private void setCustomVariable(String parameter, CustomVariable customVariable, Integer index) { - CustomVariableList cvl = (CustomVariableList) parameters.get(parameter); - if (cvl == null) { - cvl = new CustomVariableList(); - parameters.put(parameter, cvl); - } - - if (customVariable == null) { - cvl.remove(index); - if (cvl.isEmpty()) { - parameters.remove(parameter); - } - } else if (index == null) { - cvl.add(customVariable); - } else { - cvl.add(customVariable, index); - } - } - - private void removeCustomVariable(String parameter, String key) { - if (key == null) { - throw new NullPointerException("Key cannot be null."); - } - CustomVariableList cvl = (CustomVariableList) parameters.get(parameter); - if (cvl != null) { - cvl.remove(key); - if (cvl.isEmpty()) { - parameters.remove(parameter); - } - } - } - - /** - * Get the value at the specified index from the json array at the specified - * parameter. - * - * @param key the key of the json array to access - * @param index the index of the value in the json array - * @return the value at the index in the json array - */ - private JsonValue getFromJsonArray(String key, int index) { - PiwikJsonArray a = (PiwikJsonArray) parameters.get(key); - if (a == null) { - return null; - } - - return a.get(index); - } - - /** - * Add a value to the json array at the specified parameter - * - * @param key the key of the json array to add to - * @param value the value to add. Cannot be null - */ - private void addToJsonArray(String key, JsonValue value) { - if (value == null) { - throw new NullPointerException("Value cannot be null."); - } - - PiwikJsonArray a = (PiwikJsonArray) parameters.get(key); - if (a == null) { - a = new PiwikJsonArray(); - parameters.put(key, a); - } - a.add(value); - } - - /** - * Removes the json array at the specified parameter - * - * @param key the key of the json array to remove - */ - private void removeJsonArray(String key) { - parameters.remove(key); - } - - private URL returnAsUrl(String parameter, String name, String altMethod) { - Object obj = getParameter(parameter); - if (obj == null) { - return null; - } - if (obj instanceof URL) { - return (URL) obj; - } - throw new TypeConstraintException("The stored " + name + - " is a String, not a URL. Use \"" + - altMethod + "\" instead."); - } - - private String returnAsString(String parameter, String name, String altMethod) { - Object obj = getParameter(parameter); - if (obj == null) { - return null; - } - if (obj instanceof String) { - return (String) obj; - } - throw new TypeConstraintException("The stored " + name + - " is a URL, not a String. Use \"" + - altMethod + "\" instead."); + super(siteId, actionUrl); } } diff --git a/src/main/java/org/piwik/java/tracking/PiwikTracker.java b/src/main/java/org/piwik/java/tracking/PiwikTracker.java index a0ec1201..4e91e0b0 100644 --- a/src/main/java/org/piwik/java/tracking/PiwikTracker.java +++ b/src/main/java/org/piwik/java/tracking/PiwikTracker.java @@ -1,297 +1,50 @@ /* - * Piwik Java Tracker + * Matomo Java Tracker * - * @link https://github.com/piwik/piwik-java-tracker - * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause */ package org.piwik.java.tracking; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.concurrent.Future; -import javax.json.Json; -import javax.json.JsonArrayBuilder; -import javax.json.JsonObjectBuilder; -import org.apache.http.HttpResponse; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.utils.URIBuilder; -import org.apache.http.concurrent.FutureCallback; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; +import org.matomo.java.tracking.MatomoTracker; /** - * A class that sends {@link PiwikRequest}s to a specified Piwik server. - * * @author brettcsorba + * @deprecated Use {@link MatomoTracker} instead. */ -public class PiwikTracker { - private static final String AUTH_TOKEN = "token_auth"; - private static final String REQUESTS = "requests"; - private static final int DEFAULT_TIMEOUT = 5000; - private final URIBuilder uriBuilder; - private final int timeout; - private final String proxyHost; - private final int proxyPort; +@Deprecated +public class PiwikTracker extends MatomoTracker { /** - * Creates a tracker that will send {@link PiwikRequest}s to the specified - * Tracking HTTP API endpoint. - * - * @param hostUrl url endpoint to send requests to. Usually in the format - * http://your-piwik-domain.tld/piwik.php. + * @deprecated Use {@link MatomoTracker} instead. */ + @Deprecated public PiwikTracker(final String hostUrl) { - this(hostUrl, DEFAULT_TIMEOUT); - } - - /** - * Creates a tracker that will send {@link PiwikRequest}s to the specified - * Tracking HTTP API endpoint. - * - * @param hostUrl url endpoint to send requests to. Usually in the format - * http://your-piwik-domain.tld/piwik.php. - * @param timeout the timeout of the sent request in milliseconds - */ - public PiwikTracker(final String hostUrl, final int timeout) { - uriBuilder = new URIBuilder(URI.create(hostUrl)); - this.timeout = timeout; - this.proxyHost = null; - this.proxyPort = 0; - } - - /** - * Creates a tracker that will send {@link PiwikRequest}s to the specified - * Tracking HTTP API endpoint via the provided proxy - * - * @param hostUrl url endpoint to send requests to. Usually in the format - * http://your-piwik-domain.tld/piwik.php. - * @param proxyHost url endpoint for the proxy - * @param proxyPort proxy server port number - */ - public PiwikTracker(final String hostUrl, final String proxyHost, final int proxyPort) { - this(hostUrl, proxyHost, proxyPort, DEFAULT_TIMEOUT); - } - - public PiwikTracker(final String hostUrl, final String proxyHost, final int proxyPort, final int timeout) { - uriBuilder = new URIBuilder(URI.create(hostUrl)); - this.proxyHost = proxyHost; - this.proxyPort = proxyPort; - this.timeout = timeout; + super(hostUrl); } /** - * Send a request. - * - * @param request request to send - * @return the response from this request - * @throws IOException thrown if there was a problem with this connection - * @deprecated use sendRequestAsync instead + * @deprecated Use {@link MatomoTracker} instead. */ @Deprecated - public HttpResponse sendRequest(final PiwikRequest request) throws IOException { - final HttpClient client = getHttpClient(); - uriBuilder.setCustomQuery(request.getQueryString()); - HttpGet get = null; - - try { - get = new HttpGet(uriBuilder.build()); - return client.execute(get); - } catch (final URISyntaxException e) { - throw new IOException(e); - } - } - - /** - * Send a request. - * - * @param request request to send - * @return future with response from this request - * @throws IOException thrown if there was a problem with this connection - */ - public Future sendRequestAsync(final PiwikRequest request) throws IOException { - return sendRequestAsync(request, null); - } - /** - * Send a request. - * - * @param request request to send - * @param callback callback that gets executed when response arrives - * @return future with response from this request - * @throws IOException thrown if there was a problem with this connection - */ - public Future sendRequestAsync(final PiwikRequest request, FutureCallback callback) throws IOException { - final CloseableHttpAsyncClient client = getHttpAsyncClient(); - client.start(); - uriBuilder.setCustomQuery(request.getQueryString()); - HttpGet get = null; - - try { - get = new HttpGet(uriBuilder.build()); - return client.execute(get, callback); - } catch (final URISyntaxException e) { - throw new IOException(e); - } + public PiwikTracker(final String hostUrl, final int timeout) { + super(hostUrl, timeout); } /** - * Send multiple requests in a single HTTP call. More efficient than sending - * several individual requests. - * - * @param requests the requests to send - * @return the response from these requests - * @throws IOException thrown if there was a problem with this connection - * @deprecated use sendBulkRequestAsync instead + * @deprecated Use {@link MatomoTracker} instead. */ @Deprecated - public HttpResponse sendBulkRequest(final Iterable requests) throws IOException { - return sendBulkRequest(requests, null); - } - - /** - * Send multiple requests in a single HTTP call. More efficient than sending - * several individual requests. - * - * @param requests the requests to send - * @return future with response from these requests - * @throws IOException thrown if there was a problem with this connection - */ - public Future sendBulkRequestAsync(final Iterable requests) throws IOException { - return sendBulkRequestAsync(requests, null, null); - } - - /** - * Send multiple requests in a single HTTP call. More efficient than sending - * several individual requests. - * - * @param requests the requests to send - * @param callback callback that gets executed when response arrives - * @return future with response from these requests - * @throws IOException thrown if there was a problem with this connection - */ - public Future sendBulkRequestAsync(final Iterable requests, FutureCallback callback) throws IOException { - return sendBulkRequestAsync(requests, null, callback); + public PiwikTracker(final String hostUrl, final String proxyHost, final int proxyPort) { + super(hostUrl, proxyHost, proxyPort); } /** - * Send multiple requests in a single HTTP call. More efficient than sending - * several individual requests. Specify the AuthToken if parameters that require - * an auth token is used. - * - * @param requests the requests to send - * @param authToken specify if any of the parameters use require AuthToken - * @return the response from these requests - * @throws IOException thrown if there was a problem with this connection - * @deprecated use sendBulkRequestAsync instead + * @deprecated Use {@link MatomoTracker} instead. */ @Deprecated - public HttpResponse sendBulkRequest(final Iterable requests, final String authToken) throws IOException { - if (authToken != null && authToken.length() != PiwikRequest.AUTH_TOKEN_LENGTH) { - throw new IllegalArgumentException(authToken + " is not " + PiwikRequest.AUTH_TOKEN_LENGTH + " characters long."); - } - - final JsonObjectBuilder ob = Json.createObjectBuilder(); - final JsonArrayBuilder ab = Json.createArrayBuilder(); - - for (final PiwikRequest request : requests) { - ab.add("?" + request.getQueryString()); - } - - ob.add(REQUESTS, ab); - - if (authToken != null) { - ob.add(AUTH_TOKEN, authToken); - } - - final HttpClient client = getHttpClient(); - HttpPost post = null; - - try { - post = new HttpPost(uriBuilder.build()); - post.setEntity(new StringEntity(ob.build().toString(), - ContentType.APPLICATION_JSON)); - return client.execute(post); - } catch (final URISyntaxException e) { - throw new IOException(e); - } - } - - /** - * Send multiple requests in a single HTTP call. More efficient than sending - * several individual requests. Specify the AuthToken if parameters that require - * an auth token is used. - * - * @param requests the requests to send - * @param authToken specify if any of the parameters use require AuthToken - * @return the response from these requests - * @throws IOException thrown if there was a problem with this connection - */ - public Future sendBulkRequestAsync(final Iterable requests, final String authToken) throws IOException { - return sendBulkRequestAsync(requests, authToken, null); - } - - /** - * Send multiple requests in a single HTTP call. More efficient than sending - * several individual requests. Specify the AuthToken if parameters that require - * an auth token is used. - * - * @param requests the requests to send - * @param authToken specify if any of the parameters use require AuthToken - * @param callback callback that gets executed when response arrives - * @return the response from these requests - * @throws IOException thrown if there was a problem with this connection - */ - public Future sendBulkRequestAsync(final Iterable requests, final String authToken, - FutureCallback callback) throws IOException { - if (authToken != null && authToken.length() != PiwikRequest.AUTH_TOKEN_LENGTH) { - throw new IllegalArgumentException(authToken + " is not " + PiwikRequest.AUTH_TOKEN_LENGTH + " characters long."); - } - - final JsonObjectBuilder ob = Json.createObjectBuilder(); - final JsonArrayBuilder ab = Json.createArrayBuilder(); - - for (final PiwikRequest request : requests) { - ab.add("?" + request.getQueryString()); - } - - ob.add(REQUESTS, ab); - - if (authToken != null) { - ob.add(AUTH_TOKEN, authToken); - } - - final CloseableHttpAsyncClient client = getHttpAsyncClient(); - client.start(); - HttpPost post = null; - - try { - post = new HttpPost(uriBuilder.build()); - post.setEntity(new StringEntity(ob.build().toString(), - ContentType.APPLICATION_JSON)); - return client.execute(post, callback); - } catch (final URISyntaxException e) { - throw new IOException(e); - } - } - - /** - * Get a HTTP client. With proxy if a proxy is provided in the constructor. - * - * @return a HTTP client - */ - protected HttpClient getHttpClient() { - return HttpClientFactory.getInstanceFor(proxyHost, proxyPort, timeout); + public PiwikTracker(final String hostUrl, final String proxyHost, final int proxyPort, final int timeout) { + super(hostUrl, proxyHost, proxyPort, timeout); } - /** - * Get an async HTTP client. With proxy if a proxy is provided in the constructor. - * - * @return an async HTTP client - */ - protected CloseableHttpAsyncClient getHttpAsyncClient() { - return HttpClientFactory.getAsyncInstanceFor(proxyHost, proxyPort, timeout); - } } diff --git a/src/test/java/org/piwik/java/tracking/CustomVariableTest.java b/src/test/java/org/matomo/java/tracking/CustomVariableTest.java similarity index 84% rename from src/test/java/org/piwik/java/tracking/CustomVariableTest.java rename to src/test/java/org/matomo/java/tracking/CustomVariableTest.java index 4223eaed..9818bc8d 100644 --- a/src/test/java/org/piwik/java/tracking/CustomVariableTest.java +++ b/src/test/java/org/matomo/java/tracking/CustomVariableTest.java @@ -3,7 +3,7 @@ * To change this template file, choose Tools | Templates * and open the template in the editor. */ -package org.piwik.java.tracking; +package org.matomo.java.tracking; import org.junit.Before; import org.junit.Test; @@ -28,7 +28,7 @@ public void testConstructorNullKey() { new CustomVariable(null, null); fail("Exception should have been throw."); } catch (NullPointerException e) { - assertEquals("Key cannot be null.", e.getLocalizedMessage()); + assertEquals("key is marked non-null but is null", e.getLocalizedMessage()); } } @@ -38,7 +38,7 @@ public void testConstructorNullValue() { new CustomVariable("key", null); fail("Exception should have been throw."); } catch (NullPointerException e) { - assertEquals("Value cannot be null.", e.getLocalizedMessage()); + assertEquals("value is marked non-null but is null", e.getLocalizedMessage()); } } diff --git a/src/test/java/org/matomo/java/tracking/CustomVariablesTest.java b/src/test/java/org/matomo/java/tracking/CustomVariablesTest.java new file mode 100644 index 00000000..bdc4e8d1 --- /dev/null +++ b/src/test/java/org/matomo/java/tracking/CustomVariablesTest.java @@ -0,0 +1,85 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.matomo.java.tracking; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * @author Katie + */ +public class CustomVariablesTest { + private final CustomVariables customVariables = new CustomVariables(); + + @Test + public void testAdd_CustomVariable() { + CustomVariable a = new CustomVariable("a", "b"); + CustomVariable b = new CustomVariable("c", "d"); + CustomVariable c = new CustomVariable("a", "e"); + CustomVariable d = new CustomVariable("a", "f"); + + assertTrue(customVariables.isEmpty()); + customVariables.add(a); + assertFalse(customVariables.isEmpty()); + assertEquals("b", customVariables.get("a")); + assertEquals(a, customVariables.get(1)); + assertEquals("{\"1\":[\"a\",\"b\"]}", customVariables.toString()); + + customVariables.add(b); + assertEquals("d", customVariables.get("c")); + assertEquals(b, customVariables.get(2)); + assertEquals("{\"1\":[\"a\",\"b\"],\"2\":[\"c\",\"d\"]}", customVariables.toString()); + + customVariables.add(c, 5); + assertEquals("b", customVariables.get("a")); + assertEquals(c, customVariables.get(5)); + assertNull(customVariables.get(3)); + assertEquals("{\"1\":[\"a\",\"b\"],\"2\":[\"c\",\"d\"],\"5\":[\"a\",\"e\"]}", customVariables.toString()); + + customVariables.add(d); + assertEquals("f", customVariables.get("a")); + assertEquals(d, customVariables.get(1)); + assertEquals(d, customVariables.get(5)); + assertEquals("{\"1\":[\"a\",\"f\"],\"2\":[\"c\",\"d\"],\"5\":[\"a\",\"f\"]}", customVariables.toString()); + + customVariables.remove("a"); + assertNull(customVariables.get("a")); + assertNull(customVariables.get(1)); + assertNull(customVariables.get(5)); + assertEquals("{\"2\":[\"c\",\"d\"]}", customVariables.toString()); + + customVariables.remove(2); + assertNull(customVariables.get("c")); + assertNull(customVariables.get(2)); + assertTrue(customVariables.isEmpty()); + assertEquals("{}", customVariables.toString()); + } + + @Test + public void testAddCustomVariableIndexLessThan1() { + try { + customVariables.add(new CustomVariable("a", "b"), 0); + fail("Exception should have been throw."); + } catch (IllegalArgumentException e) { + assertEquals("Index must be greater than 0.", e.getLocalizedMessage()); + } + } + + @Test + public void testGetCustomVariableIntegerLessThan1() { + try { + customVariables.get(0); + fail("Exception should have been throw."); + } catch (IllegalArgumentException e) { + assertEquals("Index must be greater than 0.", e.getLocalizedMessage()); + } + } +} diff --git a/src/test/java/org/piwik/java/tracking/EcommerceItemTest.java b/src/test/java/org/matomo/java/tracking/EcommerceItemTest.java similarity index 75% rename from src/test/java/org/piwik/java/tracking/EcommerceItemTest.java rename to src/test/java/org/matomo/java/tracking/EcommerceItemTest.java index 52dbb9be..986fb305 100644 --- a/src/test/java/org/piwik/java/tracking/EcommerceItemTest.java +++ b/src/test/java/org/matomo/java/tracking/EcommerceItemTest.java @@ -1,10 +1,10 @@ /* - * Piwik Java Tracker + * Matomo Java Tracker * - * @link https://github.com/piwik/piwik-java-tracker - * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause */ -package org.piwik.java.tracking; +package org.matomo.java.tracking; import org.junit.After; import org.junit.AfterClass; @@ -12,8 +12,6 @@ import org.junit.BeforeClass; import org.junit.Test; -import javax.json.JsonValue.ValueType; - import static org.junit.Assert.assertEquals; /** @@ -106,22 +104,5 @@ public void testGetQuantity() { assertEquals(new Integer(1), ecommerceItem.getQuantity()); } - /** - * Test of getValueType method, of class EcommerceItem. - */ - @Test - public void testGetValueType() { - assertEquals(ValueType.ARRAY, ecommerceItem.getValueType()); - } - - /** - * Test of toString method, of class EcommerceItem. - */ - @Test - public void testToString() { - ecommerceItem = new EcommerceItem("sku", "name", "category", 1.0, 1); - - assertEquals("[\"sku\",\"name\",\"category\",1.0,1]", ecommerceItem.toString()); - } } diff --git a/src/test/java/org/matomo/java/tracking/EcommerceItemsTest.java b/src/test/java/org/matomo/java/tracking/EcommerceItemsTest.java new file mode 100644 index 00000000..ee76b7ce --- /dev/null +++ b/src/test/java/org/matomo/java/tracking/EcommerceItemsTest.java @@ -0,0 +1,18 @@ +package org.matomo.java.tracking; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class EcommerceItemsTest { + + @Test + public void formatsJson() { + + EcommerceItems ecommerceItems = new EcommerceItems(); + ecommerceItems.add(new EcommerceItem("sku", "name", "category", 1.0, 1)); + + assertEquals("[[\"sku\",\"name\",\"category\",1.0,1]]", ecommerceItems.toString()); + + } +} diff --git a/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java b/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java new file mode 100644 index 00000000..1583aea7 --- /dev/null +++ b/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java @@ -0,0 +1,48 @@ +package org.matomo.java.tracking; + +import org.junit.Test; + +import java.net.URL; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +public class MatomoRequestBuilderTest { + + @Test + public void buildsRequest() throws Exception { + + CustomVariable customVariable = new CustomVariable("pageCustomVariableName", "pageCustomVariableValue"); + MatomoRequest matomoRequest = MatomoRequest.builder() + .siteId(42) + .actionName("ACTION_NAME") + .actionUrl("https://www.your-domain.tld/some/page?query=foo") + .referrerUrl("https://referrer.com") + .customTrackingParameters(Collections.singletonMap("trackingParameterName", "trackingParameterValue")) + .pageCustomVariables(Collections.singletonList(customVariable)) + .visitCustomVariables(Collections.singletonList(customVariable)) + .build(); + + Map> parameters = matomoRequest.getParameters(); + assertThat(parameters.get("idsite"), hasItem((Object) 42)); + assertThat(parameters.get("action_name"), hasItem((Object) "ACTION_NAME")); + assertThat(parameters.get("apiv"), hasItem((Object) "1")); + assertThat(parameters.get("url"), hasItem((Object) new URL("https://www.your-domain.tld/some/page?query=foo"))); + assertThat(parameters.get("_id").isEmpty(), is(false)); + assertThat(parameters.get("rand").isEmpty(), is(false)); + assertThat(parameters.get("send_image"), hasItem((Object) new MatomoBoolean(false))); + assertThat(parameters.get("rec"), hasItem((Object) new MatomoBoolean(true))); + assertThat(parameters.get("urlref"), hasItem((Object) new URL("https://referrer.com"))); + assertThat(parameters.get("trackingParameterName"), hasItem((Object) "trackingParameterValue")); + CustomVariables customVariables = new CustomVariables(); + customVariables.add(customVariable); + assertThat(parameters.get("cvar"), hasItem((Object) customVariables)); + assertThat(parameters.get("_cvar"), hasItem((Object) customVariables)); + + } + +} diff --git a/src/test/java/org/piwik/java/tracking/PiwikDateTest.java b/src/test/java/org/matomo/java/tracking/PiwikDateTest.java similarity index 79% rename from src/test/java/org/piwik/java/tracking/PiwikDateTest.java rename to src/test/java/org/matomo/java/tracking/PiwikDateTest.java index 1025b5eb..0ba201d5 100644 --- a/src/test/java/org/piwik/java/tracking/PiwikDateTest.java +++ b/src/test/java/org/matomo/java/tracking/PiwikDateTest.java @@ -1,12 +1,13 @@ /* - * Piwik Java Tracker + * Matomo Java Tracker * - * @link https://github.com/piwik/piwik-java-tracker - * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause */ -package org.piwik.java.tracking; +package org.matomo.java.tracking; import org.junit.Test; +import org.piwik.java.tracking.PiwikDate; import java.util.TimeZone; diff --git a/src/test/java/org/piwik/java/tracking/PiwikLocaleTest.java b/src/test/java/org/matomo/java/tracking/PiwikLocaleTest.java similarity index 83% rename from src/test/java/org/piwik/java/tracking/PiwikLocaleTest.java rename to src/test/java/org/matomo/java/tracking/PiwikLocaleTest.java index b14c4943..1931e5e6 100644 --- a/src/test/java/org/piwik/java/tracking/PiwikLocaleTest.java +++ b/src/test/java/org/matomo/java/tracking/PiwikLocaleTest.java @@ -1,16 +1,17 @@ /* * Piwik Java Tracker * - * @link https://github.com/piwik/piwik-java-tracker - * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause */ -package org.piwik.java.tracking; +package org.matomo.java.tracking; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import org.piwik.java.tracking.PiwikLocale; import java.util.Locale; diff --git a/src/test/java/org/piwik/java/tracking/PiwikRequestTest.java b/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java similarity index 82% rename from src/test/java/org/piwik/java/tracking/PiwikRequestTest.java rename to src/test/java/org/matomo/java/tracking/PiwikRequestTest.java index 6009f7b5..b9cc3f8f 100644 --- a/src/test/java/org/piwik/java/tracking/PiwikRequestTest.java +++ b/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java @@ -1,16 +1,18 @@ /* * Piwik Java Tracker * - * @link https://github.com/piwik/piwik-java-tracker - * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause */ -package org.piwik.java.tracking; +package org.matomo.java.tracking; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.piwik.java.tracking.PiwikDate; +import org.piwik.java.tracking.PiwikLocale; +import org.piwik.java.tracking.PiwikRequest; -import javax.xml.bind.TypeConstraintException; import java.net.URL; import java.nio.charset.Charset; import java.util.List; @@ -82,25 +84,15 @@ public void testActionUrl() throws Exception { URL url = new URL("http://action.com"); request.setActionUrl(url); assertEquals(url, request.getActionUrl()); - try { - request.getActionUrlAsString(); - fail("Exception should have been thrown."); - } catch (TypeConstraintException e) { - assertEquals("The stored Action URL is a URL, not a String. Use \"getActionUrl\" instead.", e.getLocalizedMessage()); - } + assertEquals("http://action.com", request.getActionUrlAsString()); request.setActionUrlWithString(null); assertNull(request.getActionUrl()); assertNull(request.getActionUrlAsString()); - request.setActionUrlWithString("actionUrl"); - assertEquals("actionUrl", request.getActionUrlAsString()); - try { - request.getActionUrl(); - fail("Exception should have been thrown."); - } catch (TypeConstraintException e) { - assertEquals("The stored Action URL is a String, not a URL. Use \"getActionUrlAsString\" instead.", e.getLocalizedMessage()); - } + request.setActionUrlWithString("http://actionstring.com"); + assertEquals("http://actionstring.com", request.getActionUrlAsString()); + assertEquals(new URL("http://actionstring.com"), request.getActionUrl()); } /** @@ -214,23 +206,12 @@ public void testContentTarget() throws Exception { URL url = new URL("http://target.com"); request.setContentTarget(url); assertEquals(url, request.getContentTarget()); + assertEquals("http://target.com", request.getContentTargetAsString()); - try { - request.getContentTargetAsString(); - fail("Exception should have been thrown."); - } catch (TypeConstraintException e) { - assertEquals("The stored Content Target is a URL, not a String. Use \"getContentTarget\" instead.", e.getLocalizedMessage()); - } + request.setContentTargetWithString("http://targetstring.com"); + assertEquals("http://targetstring.com", request.getContentTargetAsString()); + assertEquals(new URL("http://targetstring.com"), request.getContentTarget()); - request.setContentTargetWithString("contentTarget"); - assertEquals("contentTarget", request.getContentTargetAsString()); - - try { - request.getContentTarget(); - fail("Exception should have been thrown."); - } catch (TypeConstraintException e) { - assertEquals("The stored Content Target is a String, not a URL. Use \"getContentTargetAsString\" instead.", e.getLocalizedMessage()); - } } /** @@ -269,7 +250,7 @@ public void testGetCustomTrackingParameter_T() { request.getCustomTrackingParameter(null); fail("Exception should have been thrown."); } catch (NullPointerException e) { - assertEquals("Key cannot be null.", e.getLocalizedMessage()); + assertEquals("key is marked non-null but is null", e.getLocalizedMessage()); } } @@ -284,7 +265,7 @@ public void testSetCustomTrackingParameter_T() { request.setCustomTrackingParameter(null, null); fail("Exception should have been thrown."); } catch (NullPointerException e) { - assertEquals("Key cannot be null.", e.getLocalizedMessage()); + assertEquals("key is marked non-null but is null", e.getLocalizedMessage()); } } @@ -311,7 +292,7 @@ public void testAddCustomTrackingParameter_T() { request.addCustomTrackingParameter(null, null); fail("Exception should have been thrown."); } catch (NullPointerException e) { - assertEquals("Key cannot be null.", e.getLocalizedMessage()); + assertEquals("key is marked non-null but is null", e.getLocalizedMessage()); } } @@ -321,7 +302,7 @@ public void testAddCustomTrackingParameter_FT() { request.addCustomTrackingParameter("key", null); fail("Exception should have been thrown."); } catch (NullPointerException e) { - assertEquals("Cannot add a null custom tracking parameter.", e.getLocalizedMessage()); + assertEquals("value is marked non-null but is null", e.getLocalizedMessage()); } } @@ -364,23 +345,12 @@ public void testDownloadUrl() throws Exception { URL url = new URL("http://download.com"); request.setDownloadUrl(url); assertEquals(url, request.getDownloadUrl()); + assertEquals("http://download.com", request.getDownloadUrlAsString()); - try { - request.getDownloadUrlAsString(); - fail("Exception should have been thrown."); - } catch (TypeConstraintException e) { - assertEquals("The stored Download URL is a URL, not a String. Use \"getDownloadUrl\" instead.", e.getLocalizedMessage()); - } - - request.setDownloadUrlWithString("downloadUrl"); - assertEquals("downloadUrl", request.getDownloadUrlAsString()); + request.setDownloadUrlWithString("http://downloadstring.com"); + assertEquals("http://downloadstring.com", request.getDownloadUrlAsString()); + assertEquals(new URL("http://downloadstring.com"), request.getDownloadUrl()); - try { - request.getDownloadUrl(); - fail("Exception should have been thrown."); - } catch (TypeConstraintException e) { - assertEquals("The stored Download URL is a String, not a URL. Use \"getDownloadUrlAsString\" instead.", e.getLocalizedMessage()); - } } /** @@ -402,7 +372,7 @@ public void testVerifyEcommerceEnabledT() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -414,7 +384,7 @@ public void testVerifyEcommerceEnabledFT() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -434,7 +404,7 @@ public void testVerifyEcommerceStateE() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -446,7 +416,7 @@ public void testVerifyEcommerceStateT() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("EcommerceId must be set before this value can be set.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -459,7 +429,7 @@ public void testVerifyEcommerceStateFT() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("EcommerceRevenue must be set before this value can be set.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -491,7 +461,7 @@ public void testEcommerceDiscountTE() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -520,7 +490,7 @@ public void testEcommerceIdTE() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -542,7 +512,7 @@ public void testEcommerceItemE() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -555,7 +525,7 @@ public void testEcommerceItemE2() { request.addEcommerceItem(null); fail("Exception should have been thrown."); } catch (NullPointerException e) { - assertEquals("Value cannot be null.", e.getLocalizedMessage()); + assertEquals("item is marked non-null but is null", e.getLocalizedMessage()); } } @@ -595,7 +565,7 @@ public void testEcommerceLastOrderTimestampTE() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -625,7 +595,7 @@ public void testEcommerceRevenueTE() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -656,7 +626,7 @@ public void testEcommerceShippingCostTE() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -687,7 +657,7 @@ public void testEcommerceSubtotalTE() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -718,7 +688,7 @@ public void testEcommerceTaxTE() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("GoalId must be \"0\". Try calling enableEcommerce first before calling this method.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -797,7 +767,7 @@ public void testGoalRevenueTT() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("GoalId must be set before GoalRevenue can be set.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -853,23 +823,12 @@ public void testOutlinkUrl() throws Exception { URL url = new URL("http://outlink.com"); request.setOutlinkUrl(url); assertEquals(url, request.getOutlinkUrl()); + assertEquals("http://outlink.com", request.getOutlinkUrlAsString()); - try { - request.getOutlinkUrlAsString(); - fail("Exception should have been thrown."); - } catch (TypeConstraintException e) { - assertEquals("The stored Outlink URL is a URL, not a String. Use \"getOutlinkUrl\" instead.", e.getLocalizedMessage()); - } - - request.setOutlinkUrlWithString("outlinkUrl"); - assertEquals("outlinkUrl", request.getOutlinkUrlAsString()); + request.setOutlinkUrlWithString("http://outlinkstring.com"); + assertEquals("http://outlinkstring.com", request.getOutlinkUrlAsString()); + assertEquals(new URL("http://outlinkstring.com"), request.getOutlinkUrl()); - try { - request.getOutlinkUrl(); - fail("Exception should have been thrown."); - } catch (TypeConstraintException e) { - assertEquals("The stored Outlink URL is a String, not a URL. Use \"getOutlinkUrlAsString\" instead.", e.getLocalizedMessage()); - } } /** @@ -881,7 +840,7 @@ public void testPageCustomVariableStringStringE() { request.setPageCustomVariable(null, null); fail("Exception should have been thrown"); } catch (NullPointerException e) { - assertEquals("Key cannot be null.", e.getLocalizedMessage()); + assertEquals("key is marked non-null but is null", e.getLocalizedMessage()); } } @@ -891,7 +850,7 @@ public void testPageCustomVariableStringStringE2() { request.setPageCustomVariable(null, "pageVal"); fail("Exception should have been thrown"); } catch (NullPointerException e) { - assertEquals("Key cannot be null.", e.getLocalizedMessage()); + assertEquals("key is marked non-null but is null", e.getLocalizedMessage()); } } @@ -901,7 +860,7 @@ public void testPageCustomVariableStringStringE3() { request.getPageCustomVariable(null); fail("Exception should have been thrown"); } catch (NullPointerException e) { - assertEquals("Key cannot be null.", e.getLocalizedMessage()); + assertEquals("key is marked non-null but is null", e.getLocalizedMessage()); } } @@ -1026,23 +985,12 @@ public void testReferrerUrl() throws Exception { URL url = new URL("http://referrer.com"); request.setReferrerUrl(url); assertEquals(url, request.getReferrerUrl()); + assertEquals("http://referrer.com", request.getReferrerUrlAsString()); - try { - request.getReferrerUrlAsString(); - fail("Exception should have been thrown."); - } catch (TypeConstraintException e) { - assertEquals("The stored Referrer URL is a URL, not a String. Use \"getReferrerUrl\" instead.", e.getLocalizedMessage()); - } - - request.setReferrerUrlWithString("referrerUrl"); - assertEquals("referrerUrl", request.getReferrerUrlAsString()); + request.setReferrerUrlWithString("http://referrerstring.com"); + assertEquals("http://referrerstring.com", request.getReferrerUrlAsString()); + assertEquals(new URL("http://referrerstring.com"), request.getReferrerUrl()); - try { - request.getReferrerUrl(); - fail("Exception should have been thrown."); - } catch (TypeConstraintException e) { - assertEquals("The stored Referrer URL is a String, not a URL. Use \"getReferrerUrlAsString\" instead.", e.getLocalizedMessage()); - } } /** @@ -1065,7 +1013,7 @@ public void testRequestDatetimeTTF() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("Because you are trying to set RequestDatetime for a time greater than 4 hours ago, AuthToken must be set first.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -1112,7 +1060,7 @@ public void testSearchCategoryTT() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("SearchQuery must be set before SearchCategory can be set.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -1148,7 +1096,7 @@ public void testSearchResultsCountTT() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("SearchQuery must be set before SearchResultsCount can be set.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -1201,22 +1149,22 @@ public void testVisitCustomVariableCustomVariable() { assertNull(request.getVisitCustomVariable(1)); CustomVariable cv = new CustomVariable("visitKey", "visitVal"); request.setVisitCustomVariable(cv, 1); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_cvar={\"1\":[\"visitKey\",\"visitVal\"]}&_id=1234567890123456&url=http://test.com", request.getQueryString()); + assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456&_cvar={\"1\":[\"visitKey\",\"visitVal\"]}", request.getQueryString()); request.setUserCustomVariable("key", "val"); assertEquals(cv, request.getVisitCustomVariable(1)); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_cvar={\"1\":[\"visitKey\",\"visitVal\"],\"2\":[\"key\",\"val\"]}&_id=1234567890123456&url=http://test.com", request.getQueryString()); + assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456&_cvar={\"1\":[\"visitKey\",\"visitVal\"],\"2\":[\"key\",\"val\"]}", request.getQueryString()); request.setVisitCustomVariable(null, 1); assertNull(request.getVisitCustomVariable(1)); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_cvar={\"2\":[\"key\",\"val\"]}&_id=1234567890123456&url=http://test.com", request.getQueryString()); + assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456&_cvar={\"2\":[\"key\",\"val\"]}", request.getQueryString()); request.setVisitCustomVariable(cv, 2); assertEquals(cv, request.getVisitCustomVariable(2)); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_cvar={\"2\":[\"visitKey\",\"visitVal\"]}&_id=1234567890123456&url=http://test.com", request.getQueryString()); + assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456&_cvar={\"2\":[\"visitKey\",\"visitVal\"]}", request.getQueryString()); request.setUserCustomVariable("visitKey", null); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http://test.com", request.getQueryString()); + assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456", request.getQueryString()); } /** @@ -1245,7 +1193,7 @@ public void testVisitorCityTE() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("AuthToken must be set before this value can be set.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -1275,7 +1223,7 @@ public void testVisitorCountryTE() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("AuthToken must be set before this value can be set.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -1296,7 +1244,7 @@ public void testVisitorCustomTT() { fail("Exception should have been thrown."); } catch (IllegalArgumentException e) { assertEquals("1 is not 16 characters long.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -1307,7 +1255,7 @@ public void testVisitorCustomTFT() { fail("Exception should have been thrown."); } catch (IllegalArgumentException e) { assertEquals("1234567890abcdeg is not a hexadecimal string.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -1343,7 +1291,7 @@ public void testVisitorIdTT() { fail("Exception should have been thrown."); } catch (IllegalArgumentException e) { assertEquals("1 is not 16 characters long.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -1354,7 +1302,7 @@ public void testVisitorIdTFT() { fail("Exception should have been thrown."); } catch (IllegalArgumentException e) { assertEquals("1234567890abcdeg is not a hexadecimal string.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -1388,7 +1336,7 @@ public void testVisitorIpTE() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("AuthToken must be set before this value can be set.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -1415,7 +1363,7 @@ public void testVisitorLatitudeTE() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("AuthToken must be set before this value can be set.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -1442,7 +1390,7 @@ public void testVisitorLongitudeTE() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("AuthToken must be set before this value can be set.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -1479,7 +1427,7 @@ public void testGetVisitorRegionTE() { fail("Exception should have been thrown."); } catch (IllegalStateException e) { assertEquals("AuthToken must be set before this value can be set.", - e.getLocalizedMessage()); + e.getLocalizedMessage()); } } @@ -1506,20 +1454,20 @@ public void testVisitorVisitCount() { public void testGetQueryString() { request.setRandomValue("random"); request.setVisitorId("1234567890123456"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http://test.com", request.getQueryString()); + assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456", request.getQueryString()); request.setPageCustomVariable("key", "val"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&cvar={\"1\":[\"key\",\"val\"]}&_id=1234567890123456&url=http://test.com", - request.getQueryString()); + assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456&cvar={\"1\":[\"key\",\"val\"]}", + request.getQueryString()); request.setPageCustomVariable("key", null); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http://test.com", request.getQueryString()); + assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456", request.getQueryString()); request.addCustomTrackingParameter("key", "test"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http://test.com&key=test", request.getQueryString()); + assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456&key=test", request.getQueryString()); request.addCustomTrackingParameter("key", "test2"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http://test.com&key=test&key=test2", request.getQueryString()); + assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456&key=test&key=test2", request.getQueryString()); request.setCustomTrackingParameter("key2", "test3"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http://test.com&key2=test3&key=test&key=test2", request.getQueryString()); + assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456&key=test&key=test2&key2=test3", request.getQueryString()); request.setCustomTrackingParameter("key", "test4"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http://test.com&key2=test3&key=test4", request.getQueryString()); + assertEquals("idsite=3&rec=1&url=http://test.com&apiv=1&send_image=0&rand=random&_id=1234567890123456&key2=test3&key=test4", request.getQueryString()); request.setRandomValue(null); request.setSiteId(null); request.setRequired(null); @@ -1537,7 +1485,7 @@ public void testGetQueryString2() { request.setActionUrlWithString("http://test.com"); request.setRandomValue("random"); request.setVisitorId("1234567890123456"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http://test.com", request.getQueryString()); + assertEquals("idsite=3&rec=1&apiv=1&send_image=0&url=http://test.com&rand=random&_id=1234567890123456", request.getQueryString()); } /** @@ -1547,15 +1495,15 @@ public void testGetQueryString2() { public void testGetUrlEncodedQueryString() { request.setRandomValue("random"); request.setVisitorId("1234567890123456"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http%3A%2F%2Ftest.com", request.getUrlEncodedQueryString()); + assertEquals("_id=1234567890123456&apiv=1&idsite=3&rand=random&rec=1&send_image=0&url=http%3A%2F%2Ftest.com", request.getUrlEncodedQueryString()); request.addCustomTrackingParameter("ke/y", "te:st"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http%3A%2F%2Ftest.com&ke%2Fy=te%3Ast", request.getUrlEncodedQueryString()); + assertEquals("_id=1234567890123456&apiv=1&idsite=3&ke%2Fy=te%3Ast&rand=random&rec=1&send_image=0&url=http%3A%2F%2Ftest.com", request.getUrlEncodedQueryString()); request.addCustomTrackingParameter("ke/y", "te:st2"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http%3A%2F%2Ftest.com&ke%2Fy=te%3Ast&ke%2Fy=te%3Ast2", request.getUrlEncodedQueryString()); + assertEquals("_id=1234567890123456&apiv=1&idsite=3&ke%2Fy=te%3Ast&ke%2Fy=te%3Ast2&rand=random&rec=1&send_image=0&url=http%3A%2F%2Ftest.com", request.getUrlEncodedQueryString()); request.setCustomTrackingParameter("ke/y2", "te:st3"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http%3A%2F%2Ftest.com&ke%2Fy=te%3Ast&ke%2Fy=te%3Ast2&ke%2Fy2=te%3Ast3", request.getUrlEncodedQueryString()); + assertEquals("_id=1234567890123456&apiv=1&idsite=3&ke%2Fy=te%3Ast&ke%2Fy=te%3Ast2&ke%2Fy2=te%3Ast3&rand=random&rec=1&send_image=0&url=http%3A%2F%2Ftest.com", request.getUrlEncodedQueryString()); request.setCustomTrackingParameter("ke/y", "te:st4"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http%3A%2F%2Ftest.com&ke%2Fy=te%3Ast4&ke%2Fy2=te%3Ast3", request.getUrlEncodedQueryString()); + assertEquals("_id=1234567890123456&apiv=1&idsite=3&ke%2Fy=te%3Ast4&ke%2Fy2=te%3Ast3&rand=random&rec=1&send_image=0&url=http%3A%2F%2Ftest.com", request.getUrlEncodedQueryString()); request.setRandomValue(null); request.setSiteId(null); request.setRequired(null); @@ -1573,7 +1521,7 @@ public void testGetUrlEncodedQueryString2() { request.setActionUrlWithString("http://test.com"); request.setRandomValue("random"); request.setVisitorId("1234567890123456"); - assertEquals("rand=random&idsite=3&rec=1&apiv=1&send_image=0&_id=1234567890123456&url=http%3A%2F%2Ftest.com", request.getUrlEncodedQueryString()); + assertEquals("_id=1234567890123456&apiv=1&idsite=3&rand=random&rec=1&send_image=0&url=http%3A%2F%2Ftest.com", request.getUrlEncodedQueryString()); } /** diff --git a/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java b/src/test/java/org/matomo/java/tracking/PiwikTrackerTest.java similarity index 86% rename from src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java rename to src/test/java/org/matomo/java/tracking/PiwikTrackerTest.java index f31979cd..e94354c0 100644 --- a/src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java +++ b/src/test/java/org/matomo/java/tracking/PiwikTrackerTest.java @@ -1,10 +1,10 @@ /* * Piwik Java Tracker * - * @link https://github.com/piwik/piwik-java-tracker - * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause + * @link https://github.com/matomo/matomo-java-tracker + * @license https://github.com/matomo/matomo-java-tracker/blob/master/LICENSE BSD-3 Clause */ -package org.piwik.java.tracking; +package org.matomo.java.tracking; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; @@ -22,6 +22,8 @@ import org.junit.BeforeClass; import org.junit.Test; import org.mockito.ArgumentMatcher; +import org.piwik.java.tracking.PiwikRequest; +import org.piwik.java.tracking.PiwikTracker; import java.io.IOException; import java.io.InputStream; @@ -29,12 +31,21 @@ import java.net.InetSocketAddress; import java.net.URL; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.concurrent.*; +import java.util.Map; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.doReturn; @@ -45,6 +56,8 @@ * @author brettcsorba */ public class PiwikTrackerTest { + private static final Map> PARAMETERS = Collections.singletonMap("parameterName", Collections.singleton("parameterValue")); + // https://stackoverflow.com/a/3732328 static class Handler implements HttpHandler { @Override @@ -110,9 +123,9 @@ public void testSendRequest() throws Exception { HttpResponse response = mock(HttpResponse.class); doReturn(client).when(piwikTracker).getHttpClient(); - doReturn("query").when(request).getQueryString(); + doReturn(PARAMETERS).when(request).getParameters(); doReturn(response).when(client) - .execute(argThat(new CorrectGetRequest("http://test.com?query"))); + .execute(argThat(new CorrectGetRequest("http://test.com?parameterName=parameterValue"))); assertEquals(response, piwikTracker.sendRequest(request)); } @@ -128,11 +141,11 @@ public void testSendRequestAsync() throws Exception { Future future = mock(Future.class); doReturn(client).when(piwikTracker).getHttpAsyncClient(); - doReturn("query").when(request).getQueryString(); + doReturn(PARAMETERS).when(request).getParameters(); doReturn(response).when(future).get(); doReturn(true).when(future).isDone(); doReturn(future).when(client) - .execute(argThat(new CorrectGetRequest("http://test.com?query")), any()); + .execute(argThat(new CorrectGetRequest("http://test.com?parameterName=parameterValue")), any()); assertEquals(response, piwikTracker.sendRequestAsync(request).get()); } @@ -260,7 +273,7 @@ public void testSendBulkRequest_Iterable_StringTT() throws Exception { HttpClient client = mock(HttpClient.class); PiwikRequest request = mock(PiwikRequest.class); - doReturn("query").when(request).getQueryString(); + doReturn(PARAMETERS).when(request).getParameters(); requests.add(request); doReturn(client).when(piwikTracker).getHttpClient(); @@ -278,10 +291,10 @@ public void testSendBulkRequest_Iterable_StringFF() throws Exception { PiwikRequest request = mock(PiwikRequest.class); HttpResponse response = mock(HttpResponse.class); - doReturn("query").when(request).getQueryString(); + doReturn(PARAMETERS).when(request).getParameters(); requests.add(request); doReturn(client).when(piwikTracker).getHttpClient(); - doReturn(response).when(client).execute(argThat(new CorrectPostRequest("{\"requests\":[\"?query\"]}"))); + doReturn(response).when(client).execute(argThat(new CorrectPostRequest("{\"requests\":[\"?parameterName=parameterValue\"]}"))); assertEquals(response, piwikTracker.sendBulkRequest(requests, null)); } @@ -293,11 +306,11 @@ public void testSendBulkRequest_Iterable_StringFT() throws Exception { PiwikRequest request = mock(PiwikRequest.class); HttpResponse response = mock(HttpResponse.class); - doReturn("query").when(request).getQueryString(); + doReturn(PARAMETERS).when(request).getParameters(); requests.add(request); doReturn(client).when(piwikTracker).getHttpClient(); doReturn(response).when(client) - .execute(argThat(new CorrectPostRequest("{\"requests\":[\"?query\"],\"token_auth\":\"12345678901234567890123456789012\"}"))); + .execute(argThat(new CorrectPostRequest("{\"requests\":[\"?parameterName=parameterValue\"],\"token_auth\":\"12345678901234567890123456789012\"}"))); assertEquals(response, piwikTracker.sendBulkRequest(requests, "12345678901234567890123456789012")); } @@ -328,7 +341,7 @@ public void testSendBulkRequestAsync_Iterable_StringTT() throws Exception { CloseableHttpAsyncClient client = mock(CloseableHttpAsyncClient.class); PiwikRequest request = mock(PiwikRequest.class); - doReturn("query").when(request).getQueryString(); + doReturn(PARAMETERS).when(request).getParameters(); requests.add(request); doReturn(client).when(piwikTracker).getHttpAsyncClient(); @@ -349,11 +362,11 @@ public void testSendBulkRequestAsync_Iterable_StringFF() throws Exception { doReturn(response).when(future).get(); doReturn(true).when(future).isDone(); - doReturn("query").when(request).getQueryString(); + doReturn(PARAMETERS).when(request).getParameters(); requests.add(request); doReturn(client).when(piwikTracker).getHttpAsyncClient(); doReturn(future).when(client) - .execute(argThat(new CorrectPostRequest("{\"requests\":[\"?query\"]}")), any()); + .execute(argThat(new CorrectPostRequest("{\"requests\":[\"?parameterName=parameterValue\"]}")), any()); assertEquals(response, piwikTracker.sendBulkRequestAsync(requests).get()); } @@ -368,11 +381,11 @@ public void testSendBulkRequestAsync_Iterable_StringFT() throws Exception { doReturn(response).when(future).get(); doReturn(true).when(future).isDone(); - doReturn("query").when(request).getQueryString(); + doReturn(PARAMETERS).when(request).getParameters(); requests.add(request); doReturn(client).when(piwikTracker).getHttpAsyncClient(); doReturn(future).when(client) - .execute(argThat(new CorrectPostRequest("{\"requests\":[\"?query\"],\"token_auth\":\"12345678901234567890123456789012\"}")), any()); + .execute(argThat(new CorrectPostRequest("{\"requests\":[\"?parameterName=parameterValue\"],\"token_auth\":\"12345678901234567890123456789012\"}")), any()); assertEquals(response, piwikTracker.sendBulkRequestAsync(requests, "12345678901234567890123456789012").get()); } diff --git a/src/test/java/org/piwik/java/tracking/CustomVariableListTest.java b/src/test/java/org/piwik/java/tracking/CustomVariableListTest.java deleted file mode 100644 index 76b71600..00000000 --- a/src/test/java/org/piwik/java/tracking/CustomVariableListTest.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.piwik.java.tracking; - -import org.junit.Before; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -/** - * @author Katie - */ -public class CustomVariableListTest { - private CustomVariableList cvl; - - @Before - public void setUp() { - cvl = new CustomVariableList(); - } - - @Test - public void testAdd_CustomVariable() { - CustomVariable a = new CustomVariable("a", "b"); - CustomVariable b = new CustomVariable("c", "d"); - CustomVariable c = new CustomVariable("a", "e"); - CustomVariable d = new CustomVariable("a", "f"); - - assertTrue(cvl.isEmpty()); - cvl.add(a); - assertFalse(cvl.isEmpty()); - assertEquals("b", cvl.get("a")); - assertEquals(a, cvl.get(1)); - assertEquals("{\"1\":[\"a\",\"b\"]}", cvl.toString()); - - cvl.add(b); - assertEquals("d", cvl.get("c")); - assertEquals(b, cvl.get(2)); - assertEquals("{\"1\":[\"a\",\"b\"],\"2\":[\"c\",\"d\"]}", cvl.toString()); - - cvl.add(c, 5); - assertEquals("b", cvl.get("a")); - assertEquals(c, cvl.get(5)); - assertNull(cvl.get(3)); - assertEquals("{\"1\":[\"a\",\"b\"],\"2\":[\"c\",\"d\"],\"5\":[\"a\",\"e\"]}", cvl.toString()); - - cvl.add(d); - assertEquals("f", cvl.get("a")); - assertEquals(d, cvl.get(1)); - assertEquals(d, cvl.get(5)); - assertEquals("{\"1\":[\"a\",\"f\"],\"2\":[\"c\",\"d\"],\"5\":[\"a\",\"f\"]}", cvl.toString()); - - cvl.remove("a"); - assertNull(cvl.get("a")); - assertNull(cvl.get(1)); - assertNull(cvl.get(5)); - assertEquals("{\"2\":[\"c\",\"d\"]}", cvl.toString()); - - cvl.remove(2); - assertNull(cvl.get("c")); - assertNull(cvl.get(2)); - assertTrue(cvl.isEmpty()); - assertEquals("{}", cvl.toString()); - } - - @Test - public void testAddCustomVariableIndexLessThan1() { - try { - cvl.add(new CustomVariable("a", "b"), 0); - fail("Exception should have been throw."); - } catch (IllegalArgumentException e) { - assertEquals("Index must be greater than 0.", e.getLocalizedMessage()); - } - } - - @Test - public void testGetCustomVariableIntegerLessThan1() { - try { - cvl.get(0); - fail("Exception should have been throw."); - } catch (IllegalArgumentException e) { - assertEquals("Index must be greater than 0.", e.getLocalizedMessage()); - } - } -} diff --git a/src/test/java/org/piwik/java/tracking/PiwikJsonArrayTest.java b/src/test/java/org/piwik/java/tracking/PiwikJsonArrayTest.java deleted file mode 100644 index bed3d3b1..00000000 --- a/src/test/java/org/piwik/java/tracking/PiwikJsonArrayTest.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Piwik Java Tracker - * - * @link https://github.com/piwik/piwik-java-tracker - * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause - */ -package org.piwik.java.tracking; - -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; - -import javax.json.JsonValue; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; - -/** - * @author brettcsorba - */ -public class PiwikJsonArrayTest { - PiwikJsonArray array; - - public PiwikJsonArrayTest() { - } - - @BeforeClass - public static void setUpClass() { - } - - @AfterClass - public static void tearDownClass() { - } - - @Before - public void setUp() { - array = new PiwikJsonArray(); - } - - @After - public void tearDown() { - } - - /** - * Test of get method, of class PiwikJsonArray. - */ - @Test - public void testAddGetSet() { - JsonValue value = mock(JsonValue.class); - JsonValue value2 = mock(JsonValue.class); - - array.add(value); - assertEquals(value, array.get(0)); - - array.set(0, value2); - assertEquals(value2, array.get(0)); - } - - /** - * Test of toString method, of class PiwikJsonArray. - */ - @Test - public void testToString() { - array.add(JsonValue.TRUE); - array.add(new EcommerceItem("a", "b", "c", 1.0, 2)); - array.add(JsonValue.FALSE); - - assertEquals("[true,[\"a\",\"b\",\"c\",1.0,2],false]", array.toString()); - } - -} diff --git a/src/test/java/org/piwik/java/tracking/PiwikJsonObjectTest.java b/src/test/java/org/piwik/java/tracking/PiwikJsonObjectTest.java deleted file mode 100644 index 358be490..00000000 --- a/src/test/java/org/piwik/java/tracking/PiwikJsonObjectTest.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Piwik Java Tracker - * - * @link https://github.com/piwik/piwik-java-tracker - * @license https://github.com/piwik/piwik-java-tracker/blob/master/LICENSE BSD-3 Clause - */ -package org.piwik.java.tracking; - -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -/** - * @author brettcsorba - */ -public class PiwikJsonObjectTest { - PiwikJsonObject obj; - - public PiwikJsonObjectTest() { - } - - @BeforeClass - public static void setUpClass() { - } - - @AfterClass - public static void tearDownClass() { - } - - @Before - public void setUp() { - obj = new PiwikJsonObject(); - } - - @After - public void tearDown() { - } - - /** - * Test of get method, of class PiwikJsonObject. - */ - @Test - public void testMethods() { - assertTrue(obj.isEmpty()); - assertEquals(0, obj.size()); - assertNull(obj.put("key", "value")); - assertFalse(obj.isEmpty()); - assertEquals(1, obj.size()); - assertEquals("value", obj.get("key")); - assertEquals("value", obj.remove("key")); - assertNull(obj.remove("key")); - assertTrue(obj.isEmpty()); - assertEquals(0, obj.size()); - } - - /** - * Test of toString method, of class PiwikJsonObject. - */ - @Test - public void testToString() { - obj.put("key", "value"); - obj.put("key2", "value2"); - obj.put("key3", "value3"); - obj.remove("key2"); - - assertEquals("{\"1\":[\"key\",\"value\"],\"2\":[\"key3\",\"value3\"]}", obj.toString()); - } - -} From b644fa386ec86314a18ed76fc2fca3069bf1a521 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Tue, 2 Aug 2022 15:48:01 +0200 Subject: [PATCH 2/9] Fixes #58 --- pom.xml | 9 +- .../matomo/java/tracking/MatomoRequest.java | 161 ++++++++++++------ .../java/tracking/MatomoRequestBuilder.java | 67 ++------ .../org/piwik/java/tracking/PiwikRequest.java | 5 +- .../tracking/MatomoRequestBuilderTest.java | 21 ++- .../java/tracking/PiwikRequestTest.java | 6 +- 6 files changed, 139 insertions(+), 130 deletions(-) diff --git a/pom.xml b/pom.xml index c32db240..59f84176 100644 --- a/pom.xml +++ b/pom.xml @@ -50,6 +50,7 @@ UTF-8 + 1.7.36 @@ -170,7 +171,7 @@ org.slf4j slf4j-api - 1.7.36 + ${slf4j.version} org.mockito @@ -184,6 +185,12 @@ 4.13.2 test + + org.slf4j + slf4j-simple + ${slf4j.version} + test + org.projectlombok lombok diff --git a/src/main/java/org/matomo/java/tracking/MatomoRequest.java b/src/main/java/org/matomo/java/tracking/MatomoRequest.java index 2d73c0bb..b8006720 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoRequest.java +++ b/src/main/java/org/matomo/java/tracking/MatomoRequest.java @@ -134,7 +134,7 @@ public class MatomoRequest { * @param siteId the id of the website we're tracking a visit/action for * @param actionUrl the full URL for the current action */ - public MatomoRequest(int siteId, URL actionUrl) { + public MatomoRequest(int siteId, String actionUrl) { setParameter(SITE_ID, siteId); setBooleanParameter(REQUIRED, true); setParameter(ACTION_URL, actionUrl); @@ -198,10 +198,11 @@ public void setActionTime(Long actionTime) { * Get the full URL for the current action. * * @return the full URL + * @deprecated Please use {@link #getActionUrlAsString} */ @Nullable public URL getActionUrl() { - return castOrNull(ACTION_URL); + return castToUrlOrNull(ACTION_URL); } /** @@ -209,18 +210,30 @@ public URL getActionUrl() { * * @return the full URL */ + @Deprecated @Nullable public String getActionUrlAsString() { - return getUrlParameter(ACTION_URL); + return castOrNull(ACTION_URL); } + /** + * Set the full URL for the current action. + * + * @param actionUrl the full URL to set. A null value will remove this parameter + * @deprecated Please use {@link #setActionUrl(String)} + */ + @Deprecated + public void setActionUrl(@NonNull URL actionUrl) { + setActionUrl(actionUrl.toString()); + } + /** * Set the full URL for the current action. * * @param actionUrl the full URL to set. A null value will remove this parameter */ - public void setActionUrl(URL actionUrl) { + public void setActionUrl(String actionUrl) { setParameter(ACTION_URL, actionUrl); } @@ -228,9 +241,11 @@ public void setActionUrl(URL actionUrl) { * Set the full URL for the current action. * * @param actionUrl the full URL to set. A null value will remove this parameter + * @deprecated Please use {@link #setActionUrl(String)} */ + @Deprecated public void setActionUrlWithString(String actionUrl) { - setUrlParameter(ACTION_URL, actionUrl); + setActionUrl(actionUrl); } /** @@ -414,7 +429,20 @@ public void setContentPiece(String contentPiece) { */ @Nullable public URL getContentTarget() { - return castOrNull(CONTENT_TARGET); + return castToUrlOrNull(CONTENT_TARGET); + } + + @Nullable + private URL castToUrlOrNull(@NonNull String key) { + String url = castOrNull(key); + if (url == null) { + return null; + } + try { + return new URL(url); + } catch (MalformedURLException e) { + throw new InvalidUrlException(e); + } } /** @@ -424,15 +452,26 @@ public URL getContentTarget() { */ @Nullable public String getContentTargetAsString() { - return getUrlParameter(CONTENT_TARGET); + return castOrNull(CONTENT_TARGET); } /** * Set the target of the content. For instance the URL of a landing page. * * @param contentTarget the target to set. A null value will remove this parameter + * @deprecated Please use {@link #setContentTarget(String)} */ - public void setContentTarget(URL contentTarget) { + @Deprecated + public void setContentTarget(@NonNull URL contentTarget) { + setContentTarget(contentTarget.toString()); + } + + /** + * Set the target of the content. For instance the URL of a landing page. + * + * @param contentTarget the target to set. A null value will remove this parameter + */ + public void setContentTarget(String contentTarget) { setParameter(CONTENT_TARGET, contentTarget); } @@ -440,9 +479,11 @@ public void setContentTarget(URL contentTarget) { * Set the target of the content. For instance the URL of a landing page. * * @param contentTarget the target to set. A null value will remove this parameter + * @deprecated Please use {@link #setContentTarget(String)} */ + @Deprecated public void setContentTargetWithString(String contentTarget) { - setUrlParameter(CONTENT_TARGET, contentTarget); + setContentTarget(contentTarget); } /** @@ -577,7 +618,7 @@ public void setDeviceResolution(String deviceResolution) { */ @Nullable public URL getDownloadUrl() { - return castOrNull(DOWNLOAD_URL); + return castToUrlOrNull(DOWNLOAD_URL); } /** @@ -587,7 +628,19 @@ public URL getDownloadUrl() { */ @Nullable public String getDownloadUrlAsString() { - return getUrlParameter(DOWNLOAD_URL); + return castOrNull(DOWNLOAD_URL); + } + + /** + * Set the url of a file the user has downloaded. Used for tracking downloads. + * We recommend to also set the url parameter to this same value. + * + * @param downloadUrl the url to set. A null value will remove this parameter + * @deprecated Please use {@link #setDownloadUrl(String)} + */ + @Deprecated + public void setDownloadUrl(@NonNull URL downloadUrl) { + setDownloadUrl(downloadUrl.toString()); } /** @@ -596,7 +649,7 @@ public String getDownloadUrlAsString() { * * @param downloadUrl the url to set. A null value will remove this parameter */ - public void setDownloadUrl(URL downloadUrl) { + public void setDownloadUrl(String downloadUrl) { setParameter(DOWNLOAD_URL, downloadUrl); } @@ -605,9 +658,11 @@ public void setDownloadUrl(URL downloadUrl) { * We recommend to also set the url parameter to this same value. * * @param downloadUrl the url to set. A null value will remove this parameter + * @deprecated Please use {@link #setDownloadUrl(String)} */ + @Deprecated public void setDownloadUrlWithString(String downloadUrl) { - setUrlParameter(DOWNLOAD_URL, downloadUrl); + setDownloadUrl(downloadUrl); } /** @@ -1030,7 +1085,7 @@ public void setNewVisit(Boolean newVisit) { */ @Nullable public URL getOutlinkUrl() { - return castOrNull(OUTLINK_URL); + return castToUrlOrNull(OUTLINK_URL); } /** @@ -1040,16 +1095,29 @@ public URL getOutlinkUrl() { */ @Nullable public String getOutlinkUrlAsString() { - return getUrlParameter(OUTLINK_URL); + return castOrNull(OUTLINK_URL); + } + + /** + * Set an external URL the user has opened. Used for tracking outlink clicks. + * We recommend to also set the url parameter to this same value. + * + * @param outlinkUrl the outlink url to set. A null value will remove this parameter + * @deprecated Please use {@link #setOutlinkUrl(String)} + */ + @Deprecated + public void setOutlinkUrl(@NonNull URL outlinkUrl) { + setOutlinkUrl(outlinkUrl.toString()); } + /** * Set an external URL the user has opened. Used for tracking outlink clicks. * We recommend to also set the url parameter to this same value. * * @param outlinkUrl the outlink url to set. A null value will remove this parameter */ - public void setOutlinkUrl(URL outlinkUrl) { + public void setOutlinkUrl(String outlinkUrl) { setParameter(OUTLINK_URL, outlinkUrl); } @@ -1058,9 +1126,11 @@ public void setOutlinkUrl(URL outlinkUrl) { * We recommend to also set the url parameter to this same value. * * @param outlinkUrl the outlink url to set. A null value will remove this parameter + * @deprecated Please use {@link #setOutlinkUrl(String)} */ + @Deprecated public void setOutlinkUrlWithString(String outlinkUrl) { - setUrlParameter(OUTLINK_URL, outlinkUrl); + setOutlinkUrl(outlinkUrl); } /** @@ -1313,7 +1383,7 @@ public void setRandomValue(String randomValue) { */ @Nullable public URL getReferrerUrl() { - return castOrNull(REFERRER_URL); + return castToUrlOrNull(REFERRER_URL); } /** @@ -1323,7 +1393,7 @@ public URL getReferrerUrl() { */ @Nullable public String getReferrerUrlAsString() { - return getUrlParameter(REFERRER_URL); + return castOrNull(REFERRER_URL); } /** @@ -1331,8 +1401,20 @@ public String getReferrerUrlAsString() { * got to your website (ie, through a website, search engine or campaign). * * @param referrerUrl the referrer url to set. A null value will remove this parameter + * @deprecated Please use {@link #setReferrerUrl(String)} */ - public void setReferrerUrl(URL referrerUrl) { + @Deprecated + public void setReferrerUrl(@NonNull URL referrerUrl) { + setReferrerUrl(referrerUrl.toString()); + } + + /** + * Set the full HTTP Referrer URL. This value is used to determine how someone + * got to your website (ie, through a website, search engine or campaign). + * + * @param referrerUrl the referrer url to set. A null value will remove this parameter + */ + public void setReferrerUrl(String referrerUrl) { setParameter(REFERRER_URL, referrerUrl); } @@ -1341,9 +1423,11 @@ public void setReferrerUrl(URL referrerUrl) { * got to your website (ie, through a website, search engine or campaign). * * @param referrerUrl the referrer url to set. A null value will remove this parameter + * @deprecated Please use {@link #setReferrerUrl(String)} */ + @Deprecated public void setReferrerUrlWithString(String referrerUrl) { - setUrlParameter(REFERRER_URL, referrerUrl); + setReferrerUrl(referrerUrl); } /** @@ -2000,41 +2084,6 @@ private void setBooleanParameter(@NonNull String key, @Nullable Boolean value) { } } - /** - * Get a stored parameter that is a URL. - * - * @param key the parameter's key - * @return the stored parameter's value - */ - @Nullable - private String getUrlParameter(@NonNull String key) { - URL url = castOrNull(key); - if (url == null) { - return null; - } - return url.toString(); - } - - /** - * Set a stored parameter that is a valid URL. It will be syntactically checked - * - * @param key the parameter's key - * @param url the parameter's value. Removes the parameter if null - * @throws InvalidUrlException if the URL is invalid - */ - private void setUrlParameter(@NonNull String key, @Nullable String url) { - if (url == null) { - setParameter(key, null); - } else { - try { - setParameter(key, new URL(url)); - } catch (MalformedURLException e) { - throw new InvalidUrlException(e); - } - } - - } - /** * Get a value that is stored in a json object at the specified parameter. * diff --git a/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java b/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java index 70b60ad3..39866f62 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java +++ b/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java @@ -1,7 +1,5 @@ package org.matomo.java.tracking; -import java.net.MalformedURLException; -import java.net.URL; import java.nio.charset.Charset; import java.util.List; import java.util.Map; @@ -10,7 +8,7 @@ public class MatomoRequestBuilder { private int siteId; - private URL actionUrl; + private String actionUrl; private String actionName; private Long actionTime; @@ -22,12 +20,12 @@ public class MatomoRequestBuilder { private String contentInteraction; private String contentName; private String contentPiece; - private URL contentTarget; + private String contentTarget; private Integer currentHour; private Integer currentMinute; private Integer currentSecond; private String deviceResolution; - private URL downloadUrl; + private String downloadUrl; private Double ecommerceDiscount; private String ecommerceId; private Long ecommerceLastOrderTimestamp; @@ -44,7 +42,7 @@ public class MatomoRequestBuilder { private String headerAcceptLanguage; private String headerUserAgent; private Boolean newVisit; - private URL outlinkUrl; + private String outlinkUrl; private Boolean pluginDirector; private Boolean pluginFlash; private Boolean pluginGears; @@ -55,7 +53,7 @@ public class MatomoRequestBuilder { private Boolean pluginSilverlight; private Boolean pluginWindowsMedia; private String randomValue; - private URL referrerUrl; + private String referrerUrl; private MatomoDate requestDatetime; private Boolean required; private Boolean responseAsImage; @@ -86,17 +84,8 @@ public MatomoRequestBuilder siteId(int siteId) { return this; } - public MatomoRequestBuilder actionUrl(URL actionUrl) { - this.actionUrl = actionUrl; - return this; - } - public MatomoRequestBuilder actionUrl(String actionUrl) { - try { - this.actionUrl = new URL(actionUrl); - } catch (MalformedURLException e) { - throw new InvalidUrlException(e); - } + this.actionUrl = actionUrl; return this; } @@ -150,17 +139,8 @@ public MatomoRequestBuilder contentPiece(String contentPiece) { return this; } - public MatomoRequestBuilder contentTarget(URL contentTarget) { - this.contentTarget = contentTarget; - return this; - } - public MatomoRequestBuilder contentTarget(String contentTarget) { - try { - this.contentTarget = new URL(contentTarget); - } catch (MalformedURLException e) { - throw new InvalidUrlException(e); - } + this.contentTarget = contentTarget; return this; } @@ -184,17 +164,8 @@ public MatomoRequestBuilder deviceResolution(String deviceResolution) { return this; } - public MatomoRequestBuilder downloadUrl(URL downloadUrl) { - this.downloadUrl = downloadUrl; - return this; - } - public MatomoRequestBuilder downloadUrl(String downloadUrl) { - try { - this.downloadUrl = new URL(downloadUrl); - } catch (MalformedURLException e) { - throw new InvalidUrlException(e); - } + this.downloadUrl = downloadUrl; return this; } @@ -278,17 +249,8 @@ public MatomoRequestBuilder newVisit(Boolean newVisit) { return this; } - public MatomoRequestBuilder outlinkUrl(URL outlinkUrl) { - this.outlinkUrl = outlinkUrl; - return this; - } - public MatomoRequestBuilder outlinkUrl(String outlinkUrl) { - try { - this.outlinkUrl = new URL(outlinkUrl); - } catch (MalformedURLException e) { - throw new InvalidUrlException(e); - } + this.outlinkUrl = outlinkUrl; return this; } @@ -342,17 +304,8 @@ public MatomoRequestBuilder randomValue(String randomValue) { return this; } - public MatomoRequestBuilder referrerUrl(URL referrerUrl) { - this.referrerUrl = referrerUrl; - return this; - } - public MatomoRequestBuilder referrerUrl(String referrerUrl) { - try { - this.referrerUrl = new URL(referrerUrl); - } catch (MalformedURLException e) { - throw new InvalidUrlException(e); - } + this.referrerUrl = referrerUrl; return this; } diff --git a/src/main/java/org/piwik/java/tracking/PiwikRequest.java b/src/main/java/org/piwik/java/tracking/PiwikRequest.java index b7ad9181..bab72b01 100644 --- a/src/main/java/org/piwik/java/tracking/PiwikRequest.java +++ b/src/main/java/org/piwik/java/tracking/PiwikRequest.java @@ -6,6 +6,7 @@ */ package org.piwik.java.tracking; +import lombok.NonNull; import org.matomo.java.tracking.MatomoRequest; import java.net.URL; @@ -21,7 +22,7 @@ public class PiwikRequest extends MatomoRequest { * @deprecated Use {@link MatomoRequest} instead. */ @Deprecated - public PiwikRequest(Integer siteId, URL actionUrl) { - super(siteId, actionUrl); + public PiwikRequest(int siteId, @NonNull URL actionUrl) { + super(siteId, actionUrl.toString()); } } diff --git a/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java b/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java index 1583aea7..e7d61f5f 100644 --- a/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java +++ b/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java @@ -2,7 +2,6 @@ import org.junit.Test; -import java.net.URL; import java.util.Collection; import java.util.Collections; import java.util.Map; @@ -28,20 +27,20 @@ public void buildsRequest() throws Exception { .build(); Map> parameters = matomoRequest.getParameters(); - assertThat(parameters.get("idsite"), hasItem((Object) 42)); - assertThat(parameters.get("action_name"), hasItem((Object) "ACTION_NAME")); - assertThat(parameters.get("apiv"), hasItem((Object) "1")); - assertThat(parameters.get("url"), hasItem((Object) new URL("https://www.your-domain.tld/some/page?query=foo"))); + assertThat(parameters.get("idsite"), hasItem(42)); + assertThat(parameters.get("action_name"), hasItem("ACTION_NAME")); + assertThat(parameters.get("apiv"), hasItem("1")); + assertThat(parameters.get("url"), hasItem("https://www.your-domain.tld/some/page?query=foo")); assertThat(parameters.get("_id").isEmpty(), is(false)); assertThat(parameters.get("rand").isEmpty(), is(false)); - assertThat(parameters.get("send_image"), hasItem((Object) new MatomoBoolean(false))); - assertThat(parameters.get("rec"), hasItem((Object) new MatomoBoolean(true))); - assertThat(parameters.get("urlref"), hasItem((Object) new URL("https://referrer.com"))); - assertThat(parameters.get("trackingParameterName"), hasItem((Object) "trackingParameterValue")); + assertThat(parameters.get("send_image"), hasItem(new MatomoBoolean(false))); + assertThat(parameters.get("rec"), hasItem(new MatomoBoolean(true))); + assertThat(parameters.get("urlref"), hasItem("https://referrer.com")); + assertThat(parameters.get("trackingParameterName"), hasItem("trackingParameterValue")); CustomVariables customVariables = new CustomVariables(); customVariables.add(customVariable); - assertThat(parameters.get("cvar"), hasItem((Object) customVariables)); - assertThat(parameters.get("_cvar"), hasItem((Object) customVariables)); + assertThat(parameters.get("cvar"), hasItem(customVariables)); + assertThat(parameters.get("_cvar"), hasItem(customVariables)); } diff --git a/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java b/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java index b9cc3f8f..e50fc017 100644 --- a/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java +++ b/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java @@ -77,7 +77,7 @@ public void testActionTime() { */ @Test public void testActionUrl() throws Exception { - request.setActionUrl(null); + request.setActionUrl((String) null); assertNull(request.getActionUrl()); assertNull(request.getActionUrlAsString()); @@ -1474,7 +1474,7 @@ public void testGetQueryString() { request.setApiVersion(null); request.setResponseAsImage(null); request.setVisitorId(null); - request.setActionUrl(null); + request.setActionUrl((String) null); assertEquals("key2=test3&key=test4", request.getQueryString()); request.clearCustomTrackingParameter(); assertEquals("", request.getQueryString()); @@ -1510,7 +1510,7 @@ public void testGetUrlEncodedQueryString() { request.setApiVersion(null); request.setResponseAsImage(null); request.setVisitorId(null); - request.setActionUrl(null); + request.setActionUrl((String) null); assertEquals("ke%2Fy=te%3Ast4&ke%2Fy2=te%3Ast3", request.getUrlEncodedQueryString()); request.clearCustomTrackingParameter(); assertEquals("", request.getUrlEncodedQueryString()); From 26a767f189f25b498f77b4a73927ff33e43f01fb Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Tue, 2 Aug 2022 15:59:57 +0200 Subject: [PATCH 3/9] Fixes #41 --- .../matomo/java/tracking/MatomoRequest.java | 21 +++++++++++++++++++ .../java/tracking/MatomoRequestBuilder.java | 10 +++++++++ .../tracking/MatomoRequestBuilderTest.java | 4 +++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/matomo/java/tracking/MatomoRequest.java b/src/main/java/org/matomo/java/tracking/MatomoRequest.java index b8006720..d2d1df29 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoRequest.java +++ b/src/main/java/org/matomo/java/tracking/MatomoRequest.java @@ -55,6 +55,8 @@ public class MatomoRequest { private static final String CURRENT_HOUR = "h"; private static final String CURRENT_MINUTE = "m"; private static final String CURRENT_SECOND = "s"; + + private static final String CUSTOM_ACTION = "ca"; private static final String DEVICE_RESOLUTION = "res"; private static final String DOWNLOAD_URL = "download"; private static final String ECOMMERCE_DISCOUNT = "ec_dt"; @@ -543,6 +545,25 @@ public void setCurrentSecond(Integer currentSecond) { setParameter(CURRENT_SECOND, currentSecond); } + /** + * Get the custom action + * + * @return the custom action + */ + @Nullable + public Boolean getCustomAction() { + return getBooleanParameter(CUSTOM_ACTION); + } + + /** + * Set the custom action + * + * @param customAction the second to set. A null value will remove this parameter + */ + public void setCustomAction(Boolean customAction) { + setBooleanParameter(CUSTOM_ACTION, customAction); + } + /** * Gets the list of objects currently stored at the specified custom tracking * parameter. An empty list will be returned if there are no objects set at diff --git a/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java b/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java index 39866f62..712a0be9 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java +++ b/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java @@ -24,6 +24,8 @@ public class MatomoRequestBuilder { private Integer currentHour; private Integer currentMinute; private Integer currentSecond; + + private Boolean customAction; private String deviceResolution; private String downloadUrl; private Double ecommerceDiscount; @@ -159,6 +161,11 @@ public MatomoRequestBuilder currentSecond(Integer currentSecond) { return this; } + public MatomoRequestBuilder customAction(Boolean customAction) { + this.customAction = customAction; + return this; + } + public MatomoRequestBuilder deviceResolution(String deviceResolution) { this.deviceResolution = deviceResolution; return this; @@ -463,6 +470,9 @@ public MatomoRequest build() { if (currentSecond != null) { matomoRequest.setCurrentSecond(currentSecond); } + if (customAction != null) { + matomoRequest.setCustomAction(customAction); + } if (customTrackingParameters != null) { for (Map.Entry customTrackingParameter : customTrackingParameters.entrySet()) { matomoRequest.addCustomTrackingParameter(customTrackingParameter.getKey(), customTrackingParameter.getValue()); diff --git a/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java b/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java index e7d61f5f..9d2804ed 100644 --- a/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java +++ b/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java @@ -13,7 +13,7 @@ public class MatomoRequestBuilderTest { @Test - public void buildsRequest() throws Exception { + public void buildsRequest() { CustomVariable customVariable = new CustomVariable("pageCustomVariableName", "pageCustomVariableValue"); MatomoRequest matomoRequest = MatomoRequest.builder() @@ -24,6 +24,7 @@ public void buildsRequest() throws Exception { .customTrackingParameters(Collections.singletonMap("trackingParameterName", "trackingParameterValue")) .pageCustomVariables(Collections.singletonList(customVariable)) .visitCustomVariables(Collections.singletonList(customVariable)) + .customAction(true) .build(); Map> parameters = matomoRequest.getParameters(); @@ -41,6 +42,7 @@ public void buildsRequest() throws Exception { customVariables.add(customVariable); assertThat(parameters.get("cvar"), hasItem(customVariables)); assertThat(parameters.get("_cvar"), hasItem(customVariables)); + assertThat(parameters.get("ca"), hasItem(new MatomoBoolean(true))); } From 3d90390db1bb605541759fba48b4be89cf357335 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 8 Aug 2022 14:46:49 +0200 Subject: [PATCH 4/9] Removes unused method --- src/main/java/org/matomo/java/tracking/EcommerceItems.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/org/matomo/java/tracking/EcommerceItems.java b/src/main/java/org/matomo/java/tracking/EcommerceItems.java index 9f435535..a27ae2f2 100644 --- a/src/main/java/org/matomo/java/tracking/EcommerceItems.java +++ b/src/main/java/org/matomo/java/tracking/EcommerceItems.java @@ -31,10 +31,6 @@ public EcommerceItem get(int index) { return ecommerceItems.get(index); } - public EcommerceItem set(int index, EcommerceItem element) { - return ecommerceItems.set(index, element); - } - @Override public String toString() { ArrayNode arrayNode = OBJECT_MAPPER.createArrayNode(); From 88a4730673c35254c02e3761af3959184d9cbd93 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 8 Aug 2022 15:44:08 +0200 Subject: [PATCH 5/9] Removes unused exception --- src/main/java/org/matomo/java/tracking/MatomoTracker.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/org/matomo/java/tracking/MatomoTracker.java b/src/main/java/org/matomo/java/tracking/MatomoTracker.java index 4b0d8882..9d2ef7ae 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoTracker.java +++ b/src/main/java/org/matomo/java/tracking/MatomoTracker.java @@ -229,9 +229,8 @@ public Future sendBulkRequestAsync(final Iterable sendBulkRequestAsync(final Iterable requests, final String authToken, FutureCallback callback) throws IOException { + public Future sendBulkRequestAsync(final Iterable requests, final String authToken, FutureCallback callback) { if (authToken != null && authToken.length() != MatomoRequest.AUTH_TOKEN_LENGTH) { throw new IllegalArgumentException(authToken + " is not " + MatomoRequest.AUTH_TOKEN_LENGTH + " characters long."); } From 4800dc67a003deb6729c2b9d765a63279de41be1 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 8 Aug 2022 15:44:17 +0200 Subject: [PATCH 6/9] Enhance README --- README.md | 251 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 218 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 175813f9..7b325aa1 100644 --- a/README.md +++ b/README.md @@ -15,17 +15,48 @@ releases can be found [here](https://matomo-org.github.io/matomo-java-tracker/ja ## Using this API +### Add library to your build + +Add a dependency on Matomo Java Tracker using Maven: + +```xml + + + org.piwik.java.tracking + matomo-java-tracker + 2.0 + +``` + +or Gradle: + +```groovy +dependencies { + implementation("org.piwik.java.tracking:matomo-java-tracker:2.0") +} +``` + ### Create a Request Each MatomoRequest represents an action the user has taken that you want tracked by your Matomo server. Create a MatomoRequest through ```java -MatomoRequest request=MatomoRequest.builder() - .siteId(42) - .actionUrl("https://www.mydomain.com/some/site") - .actionName("Signup") - .build(); + +import org.matomo.java.tracking.MatomoRequest; + +public class YourImplementation { + + public void yourMethod() { + MatomoRequest request = MatomoRequest.builder() + .siteId(42) + .actionUrl("https://www.mydomain.com/signup") + .actionName("Signup") + .build(); + } + +} + ``` Per default every request has the following default parameters: @@ -40,76 +71,230 @@ Per default every request has the following default parameters: Overwrite these properties as desired. -Note that if you want to be able to track campaigns using Referrers > Campaigns, you must add the correct +Note that if you want to be able to track campaigns using *Referrers > Campaigns*, you must add the correct URL parameters to your actionUrl. For example, ```java -URL actionUrl=new URL("http://example.org/landing.html?pk_campaign=Email-Nov2011&pk_kwd=LearnMore"); + +package example; + +import org.matomo.java.tracking.MatomoRequest; + +public class YourImplementation { + + public void yourMethod() { + + MatomoRequest request = MatomoRequest.builder() + .siteId(42) + .actionUrl("http://example.org/landing.html?pk_campaign=Email-Nov2011&pk_kwd=LearnMore") // include the query parameters to the url + .actionName("LearnMore") + .build(); + } + +} ``` See [Tracking Campaigns](https://matomo.org/docs/tracking-campaigns/) for more information. All HTTP query parameters denoted on the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api) can be set using the appropriate -getters and setters. See MatomoRequest.java for the mappings of the parameters to their corresponding +getters and setters. See _MatomoRequest.java_ for the mappings of the parameters to their corresponding Java getters/setters. Some parameters are dependent on the state of other parameters: -EcommerceEnabled must be called before the following parameters are set: EcommerceId and -EcommerceRevenue. +_EcommerceEnabled_ must be called before the following parameters are set: *EcommerceId* and * +EcommerceRevenue*. -EcommerceId and EcommerceRevenue must be set before the following parameters are -set: EcommerceDiscount, EcommerceItem, EcommerceLastOrderTimestamp, -EcommerceShippingCost, EcommerceSubtotal, and EcommerceTax. +_EcommerceId_ and _EcommerceRevenue_ must be set before the following parameters are +set: *EcommerceDiscount*, *EcommerceItem*, *EcommerceLastOrderTimestamp*, * +EcommerceShippingCost*, *EcommerceSubtotal*, and *EcommerceTax*. -AuthToken must be set before the following parameters are set: VisitorCity, -VisitorCountry, VisitorIp, VisitorLatitude, VisitorLongitude, and VisitorRegion +_AuthToken_ must be set before the following parameters are set: *VisitorCity*, * +VisitorCountry*, *VisitorIp*, *VisitorLatitude*, *VisitorLongitude*, and *VisitorRegion* . ### Sending Requests -Create a MatomoTracker through +Create a MatomoTracker using the constructor ```java -MatomoTracker tracker=new MatomoTracker(hostUrl); +package example; + +import org.matomo.java.tracking.MatomoTracker; + +public class YourImplementation { + + public void yourMethod() { + + MatomoTracker tracker = new MatomoTracker("https://your-matomo-domain.tld/matomo.php"); + + } + +} ``` -where hostUrl is the url endpoint of the Matomo server. Usually in the format http://your-matomo-domain.tld/matomo.php. +using the Matomo Endpoint URL as the first parameter. To send a single request, call ```java -Future response=tracker.sendRequestAsync(request); +package example; + +import org.apache.http.HttpResponse; +import org.matomo.java.tracking.MatomoRequest; +import org.matomo.java.tracking.MatomoTracker; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +public class YourImplementation { + + public void yourMethod() { + + MatomoRequest request = MatomoRequest.builder().siteId(42).actionUrl("https://www.mydomain.com/some/page").actionName("Signup").build(); + + MatomoTracker tracker = new MatomoTracker("https://your-matomo-domain.tld/matomo.php"); + try { + Future response = tracker.sendRequestAsync(request); + // usually not needed: + HttpResponse httpResponse = response.get(); + int statusCode = httpResponse.getStatusLine().getStatusCode(); + if (statusCode > 399) { + // problem + } + } catch (IOException e) { + throw new UncheckedIOException("Could not send request to Matomo", e); + } catch (ExecutionException | InterruptedException e) { + throw new RuntimeException("Error while getting response", 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, -send a bulk request. Place your requests in an Iterable data structure and call +send a bulk request. Place your requests in an _Iterable_ data structure and call ```java -Future response=tracker.sendBulkRequestAsync(requests); +package example; + +import org.apache.http.HttpResponse; +import org.matomo.java.tracking.MatomoRequest; +import org.matomo.java.tracking.MatomoRequestBuilder; +import org.matomo.java.tracking.MatomoTracker; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +public class YourImplementation { + + public void yourMethod() { + + Collection requests = new ArrayList<>(); + MatomoRequestBuilder builder = MatomoRequest.builder().siteId(42); + requests.add(builder.actionUrl("https://www.mydomain.com/some/page").actionName("Some Page").build()); + requests.add(builder.actionUrl("https://www.mydomain.com/another/page").actionName("Another Page").build()); + + MatomoTracker tracker = new MatomoTracker("https://your-matomo-domain.tld/matomo.php"); + try { + Future response = tracker.sendBulkRequestAsync(requests); + // usually not needed: + HttpResponse httpResponse = response.get(); + int statusCode = httpResponse.getStatusLine().getStatusCode(); + if (statusCode > 399) { + // problem + } + } catch (IOException e) { + throw new UncheckedIOException("Could not send request to Matomo", e); + } catch (ExecutionException | InterruptedException e) { + throw new RuntimeException("Error while getting response", e); + } + + } + +} + ``` If some of the parameters that you've specified in the bulk request require AuthToken to be set, this can also be set in the bulk request through ```java -Future response=tracker.sendBulkRequestAsync(requests,authToken); +package example; + +import org.apache.http.HttpResponse; +import org.matomo.java.tracking.MatomoLocale; +import org.matomo.java.tracking.MatomoRequest; +import org.matomo.java.tracking.MatomoRequestBuilder; +import org.matomo.java.tracking.MatomoTracker; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Locale; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +public class YourImplementation { + + public void yourMethod() { + + Collection requests = new ArrayList<>(); + MatomoRequestBuilder builder = MatomoRequest.builder().siteId(42); + requests.add(builder.actionUrl("https://www.mydomain.com/some/page").actionName("Some Page").build()); + requests.add(builder.actionUrl("https://www.mydomain.com/another/page").actionName("Another Page").visitorCountry(new MatomoLocale(Locale.GERMANY)).build()); + + MatomoTracker tracker = new MatomoTracker("https://your-matomo-domain.tld/matomo.php"); + try { + Future response = tracker.sendBulkRequestAsync(requests, "33dc3f2536d3025974cccb4b4d2d98f4"); // second parameter is authentication token need for country override + // usually not needed: + HttpResponse httpResponse = response.get(); + int statusCode = httpResponse.getStatusLine().getStatusCode(); + if (statusCode > 399) { + // problem + } + } catch (IOException e) { + throw new UncheckedIOException("Could not send request to Matomo", e); + } catch (ExecutionException | InterruptedException e) { + throw new RuntimeException("Error while getting response", e); + } + + } + +} + + ``` -## Install +## Building + +You need a GPG signing key on your machine. Please follow these +instructions: https://docs.github.com/en/authentication/managing-commit-signature-verification/generating-a-new-gpg-key This project can be tested and built by calling ```shell -mvn package +mvn install ``` -The built jars and javadoc can be found in target +The built jars and javadoc can be found in `target`. By using the install Maven goal, the snapshot +version can be used using your local Maven repository for testing purposes, e.g. -Test this project using +```xml -```shell -mvn test + + org.piwik.java.tracking + matomo-java-tracker + 2.1-SNAPSHOT + ``` This project also supports [Pitest](http://pitest.org/) mutation testing. This report can be generated by calling @@ -118,7 +303,7 @@ This project also supports [Pitest](http://pitest.org/) mutation testing. This r mvn org.pitest:pitest-maven:mutationCoverage ``` -and will produce an HTML report at target/pit-reports/YYYYMMDDHHMI +and will produce an HTML report at `target/pit-reports/YYYYMMDDHHMI` Clean this project using @@ -132,12 +317,12 @@ Have a fantastic feature idea? Spot a bug? We would absolutely love for you to c free to: * Fork this project -* Create a feature branch from the master branch +* Create a feature branch from the _master_ branch * Write awesome code that does awesome things * Write awesome test to test your awesome code -* Verify that everything is working as it should by running mvn test. If everything passes, you may - want to make sure that your tests are covering everything you think they are! Run mvn org.pitest: - pitest-maven:mutationCoverage to find out! +* Verify that everything is working as it should by running _mvn test_. If everything passes, you may + want to make sure that your tests are covering everything you think they are! + Run `mvn org.pitest:pitest-maven:mutationCoverage` to find out! * Commit this code to your repository * Submit a pull request from your branch to our dev branch and let us know why you made the changes you did * We'll take a look at your request and work to get it integrated with the repo! From 3579e46cc6d32e0b0715f7a53a715acf00a68a8d Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 8 Aug 2022 16:10:14 +0200 Subject: [PATCH 7/9] Use unchecked exceptions --- .../matomo/java/tracking/MatomoException.java | 10 +++ .../matomo/java/tracking/MatomoTracker.java | 72 +++++++++++-------- 2 files changed, 51 insertions(+), 31 deletions(-) create mode 100644 src/main/java/org/matomo/java/tracking/MatomoException.java diff --git a/src/main/java/org/matomo/java/tracking/MatomoException.java b/src/main/java/org/matomo/java/tracking/MatomoException.java new file mode 100644 index 00000000..39ea492e --- /dev/null +++ b/src/main/java/org/matomo/java/tracking/MatomoException.java @@ -0,0 +1,10 @@ +package org.matomo.java.tracking; + +public class MatomoException extends RuntimeException { + + private static final long serialVersionUID = 4592083764365938934L; + + public MatomoException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/org/matomo/java/tracking/MatomoTracker.java b/src/main/java/org/matomo/java/tracking/MatomoTracker.java index 9d2ef7ae..4ac30954 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoTracker.java +++ b/src/main/java/org/matomo/java/tracking/MatomoTracker.java @@ -9,6 +9,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.NonNull; import lombok.extern.slf4j.Slf4j; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; @@ -21,6 +22,8 @@ import org.apache.http.entity.StringEntity; import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; @@ -49,9 +52,9 @@ public class MatomoTracker { * Tracking HTTP API endpoint. * * @param hostUrl url endpoint to send requests to. Usually in the format - * http://your-matomo-domain.tld/matomo.php. + * http://your-matomo-domain.tld/matomo.php. Must not be null */ - public MatomoTracker(final String hostUrl) { + public MatomoTracker(@NonNull final String hostUrl) { this(hostUrl, DEFAULT_TIMEOUT); } @@ -63,11 +66,11 @@ public MatomoTracker(final String hostUrl) { * http://your-matomo-domain.tld/matomo.php. * @param timeout the timeout of the sent request in milliseconds */ - public MatomoTracker(final String hostUrl, final int timeout) { + public MatomoTracker(@NonNull final String hostUrl, final int timeout) { this(hostUrl, null, 0, timeout); } - public MatomoTracker(final String hostUrl, final String proxyHost, final int proxyPort, final int timeout) { + public MatomoTracker(@NonNull final String hostUrl, @Nullable final String proxyHost, final int proxyPort, final int timeout) { this.hostUrl = URI.create(hostUrl); this.proxyHost = proxyHost; this.proxyPort = proxyPort; @@ -83,27 +86,36 @@ public MatomoTracker(final String hostUrl, final String proxyHost, final int pro * @param proxyHost url endpoint for the proxy * @param proxyPort proxy server port number */ - public MatomoTracker(final String hostUrl, final String proxyHost, final int proxyPort) { + public MatomoTracker(@NonNull final String hostUrl, @Nullable final String proxyHost, final int proxyPort) { this(hostUrl, proxyHost, proxyPort, DEFAULT_TIMEOUT); } /** - * Send a request. + * Sends a tracking request to Matomo * - * @param request request to send + * @param request request to send. must not be null * @return the response from this request * @throws IOException thrown if there was a problem with this connection * @deprecated use sendRequestAsync instead */ @Deprecated - public HttpResponse sendRequest(final MatomoRequest request) throws IOException { + public HttpResponse sendRequest(@NonNull final MatomoRequest request) { final HttpClient client = getHttpClient(); + HttpUriRequest get = createGetRequest(request); + log.debug("Sending request via GET: {}", request); try { - HttpUriRequest get = new HttpGet(new URIBuilder(hostUrl).addParameters(QueryParameters.fromMap(request.getParameters())).build()); - log.debug("Sending request via GET: {}", request); return client.execute(get); - } catch (final URISyntaxException e) { - throw new IOException(e); + } catch (IOException e) { + throw new MatomoException("Could not send request to Matomo", e); + } + } + + @Nonnull + private HttpUriRequest createGetRequest(@NonNull MatomoRequest request) { + try { + return new HttpGet(new URIBuilder(hostUrl).addParameters(QueryParameters.fromMap(request.getParameters())).build()); + } catch (URISyntaxException e) { + throw new InvalidUrlException(e); } } @@ -123,7 +135,7 @@ protected HttpClient getHttpClient() { * @return future with response from this request * @throws IOException thrown if there was a problem with this connection */ - public Future sendRequestAsync(final MatomoRequest request) throws IOException { + public Future sendRequestAsync(@NonNull final MatomoRequest request) { return sendRequestAsync(request, null); } @@ -135,17 +147,12 @@ public Future sendRequestAsync(final MatomoRequest request) throws * @return future with response from this request * @throws IOException thrown if there was a problem with this connection */ - public Future sendRequestAsync(final MatomoRequest request, FutureCallback callback) throws IOException { + public Future sendRequestAsync(@NonNull final MatomoRequest request, @Nullable FutureCallback callback) { final CloseableHttpAsyncClient client = getHttpAsyncClient(); client.start(); - - try { - HttpUriRequest get = new HttpGet(new URIBuilder(hostUrl).addParameters(QueryParameters.fromMap(request.getParameters())).build()); - log.debug("Sending async request via GET: {}", request); - return client.execute(get, callback); - } catch (final URISyntaxException e) { - throw new IOException(e); - } + HttpUriRequest get = createGetRequest(request); + log.debug("Sending async request via GET: {}", request); + return client.execute(get, callback); } /** @@ -167,7 +174,7 @@ protected CloseableHttpAsyncClient getHttpAsyncClient() { * @deprecated use sendBulkRequestAsync instead */ @Deprecated - public HttpResponse sendBulkRequest(final Iterable requests) throws IOException { + public HttpResponse sendBulkRequest(@NonNull final Iterable requests) { return sendBulkRequest(requests, null); } @@ -183,18 +190,21 @@ public HttpResponse sendBulkRequest(final Iterable requ * @deprecated use sendBulkRequestAsync instead */ @Deprecated - public HttpResponse sendBulkRequest(final Iterable requests, final String authToken) throws IOException { + public HttpResponse sendBulkRequest(@NonNull final Iterable requests, @Nullable final String authToken) { if (authToken != null && authToken.length() != MatomoRequest.AUTH_TOKEN_LENGTH) { throw new IllegalArgumentException(authToken + " is not " + MatomoRequest.AUTH_TOKEN_LENGTH + " characters long."); } - HttpPost post = buildPost(requests, authToken); final HttpClient client = getHttpClient(); log.debug("Sending requests via POST: {}", requests); - return client.execute(post); + try { + return client.execute(post); + } catch (IOException e) { + throw new MatomoException("Could not send bulk request", e); + } } - private HttpPost buildPost(Iterable requests, String authToken) { + private HttpPost buildPost(@NonNull Iterable requests, @Nullable String authToken) { ObjectNode objectNode = OBJECT_MAPPER.createObjectNode(); ArrayNode requestsNode = objectNode.putArray(REQUESTS); for (final MatomoRequest request : requests) { @@ -216,7 +226,7 @@ private HttpPost buildPost(Iterable requests, String au * @return future with response from these requests * @throws IOException thrown if there was a problem with this connection */ - public Future sendBulkRequestAsync(final Iterable requests) throws IOException { + public Future sendBulkRequestAsync(@NonNull final Iterable requests) { return sendBulkRequestAsync(requests, null, null); } @@ -230,7 +240,7 @@ public Future sendBulkRequestAsync(final Iterable sendBulkRequestAsync(final Iterable requests, final String authToken, FutureCallback callback) { + public Future sendBulkRequestAsync(@NonNull final Iterable requests, @Nullable final String authToken, @Nullable FutureCallback callback) { if (authToken != null && authToken.length() != MatomoRequest.AUTH_TOKEN_LENGTH) { throw new IllegalArgumentException(authToken + " is not " + MatomoRequest.AUTH_TOKEN_LENGTH + " characters long."); } @@ -250,7 +260,7 @@ public Future sendBulkRequestAsync(final Iterable sendBulkRequestAsync(final Iterable requests, FutureCallback callback) throws IOException { + public Future sendBulkRequestAsync(@NonNull final Iterable requests, @Nullable FutureCallback callback) { return sendBulkRequestAsync(requests, null, callback); } @@ -264,7 +274,7 @@ public Future sendBulkRequestAsync(final Iterable sendBulkRequestAsync(final Iterable requests, final String authToken) throws IOException { + public Future sendBulkRequestAsync(@NonNull final Iterable requests, @Nullable final String authToken) { return sendBulkRequestAsync(requests, authToken, null); } } From c41ef4e5d85d353897839b3d3f679d07360f38b3 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 8 Aug 2022 16:11:24 +0200 Subject: [PATCH 8/9] Remove unnecessary throws declarations --- .../java/org/matomo/java/tracking/PiwikTrackerTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/org/matomo/java/tracking/PiwikTrackerTest.java b/src/test/java/org/matomo/java/tracking/PiwikTrackerTest.java index e94354c0..b37a9ebc 100644 --- a/src/test/java/org/matomo/java/tracking/PiwikTrackerTest.java +++ b/src/test/java/org/matomo/java/tracking/PiwikTrackerTest.java @@ -254,7 +254,7 @@ public boolean matches(HttpGet get) { * Test of sendBulkRequest method, of class PiwikTracker. */ @Test - public void testSendBulkRequest_Iterable() throws Exception { + public void testSendBulkRequest_Iterable() { List requests = new ArrayList<>(); HttpResponse response = mock(HttpResponse.class); @@ -267,7 +267,7 @@ public void testSendBulkRequest_Iterable() throws Exception { * Test of sendBulkRequest method, of class PiwikTracker. */ @Test - public void testSendBulkRequest_Iterable_StringTT() throws Exception { + public void testSendBulkRequest_Iterable_StringTT() { try { List requests = new ArrayList<>(); HttpClient client = mock(HttpClient.class); @@ -335,7 +335,7 @@ public void testSendBulkRequestAsync_Iterable() throws Exception { * Test of sendBulkRequestAsync method, of class PiwikTracker. */ @Test - public void testSendBulkRequestAsync_Iterable_StringTT() throws Exception { + public void testSendBulkRequestAsync_Iterable_StringTT() { try { List requests = new ArrayList<>(); CloseableHttpAsyncClient client = mock(CloseableHttpAsyncClient.class); From 8b456c9b7cb3c7280a2c190bb9e43c389b856df1 Mon Sep 17 00:00:00 2001 From: Daniel Heid Date: Mon, 8 Aug 2022 16:15:53 +0200 Subject: [PATCH 9/9] Removes invalid Javadoc --- .../java/org/matomo/java/tracking/HttpClientFactory.java | 2 +- src/main/java/org/matomo/java/tracking/MatomoRequest.java | 2 +- src/main/java/org/matomo/java/tracking/MatomoTracker.java | 8 -------- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/matomo/java/tracking/HttpClientFactory.java b/src/main/java/org/matomo/java/tracking/HttpClientFactory.java index 1ae896d8..86dca94f 100644 --- a/src/main/java/org/matomo/java/tracking/HttpClientFactory.java +++ b/src/main/java/org/matomo/java/tracking/HttpClientFactory.java @@ -16,7 +16,7 @@ /** * Internal factory for providing instances of HTTP clients. - * Especially {@linkAsyncHttpClient} instances are intended to be global resources that share the same lifecycle as the application. + * Especially {@link org.apache.http.nio.client.HttpAsyncClient} instances are intended to be global resources that share the same lifecycle as the application. * For details see Apache documentation. * * @author norbertroamsys diff --git a/src/main/java/org/matomo/java/tracking/MatomoRequest.java b/src/main/java/org/matomo/java/tracking/MatomoRequest.java index d2d1df29..f3681161 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoRequest.java +++ b/src/main/java/org/matomo/java/tracking/MatomoRequest.java @@ -2134,8 +2134,8 @@ private String getCustomVariable(@NonNull String parameter, @NonNull String key) * Store a value in a json object at the specified parameter. * * @param parameter the parameter to store the json object at - * @param parameter the key of the value. Cannot be null * @param customVariable the value. Removes the parameter if null + * @param index the custom variable index */ private void setCustomVariable(@NonNull String parameter, @Nullable CustomVariable customVariable, Integer index) { diff --git a/src/main/java/org/matomo/java/tracking/MatomoTracker.java b/src/main/java/org/matomo/java/tracking/MatomoTracker.java index 4ac30954..d5995beb 100644 --- a/src/main/java/org/matomo/java/tracking/MatomoTracker.java +++ b/src/main/java/org/matomo/java/tracking/MatomoTracker.java @@ -95,7 +95,6 @@ public MatomoTracker(@NonNull final String hostUrl, @Nullable final String proxy * * @param request request to send. must not be null * @return the response from this request - * @throws IOException thrown if there was a problem with this connection * @deprecated use sendRequestAsync instead */ @Deprecated @@ -133,7 +132,6 @@ protected HttpClient getHttpClient() { * * @param request request to send * @return future with response from this request - * @throws IOException thrown if there was a problem with this connection */ public Future sendRequestAsync(@NonNull final MatomoRequest request) { return sendRequestAsync(request, null); @@ -145,7 +143,6 @@ public Future sendRequestAsync(@NonNull final MatomoRequest reques * @param request request to send * @param callback callback that gets executed when response arrives * @return future with response from this request - * @throws IOException thrown if there was a problem with this connection */ public Future sendRequestAsync(@NonNull final MatomoRequest request, @Nullable FutureCallback callback) { final CloseableHttpAsyncClient client = getHttpAsyncClient(); @@ -170,7 +167,6 @@ protected CloseableHttpAsyncClient getHttpAsyncClient() { * * @param requests the requests to send * @return the response from these requests - * @throws IOException thrown if there was a problem with this connection * @deprecated use sendBulkRequestAsync instead */ @Deprecated @@ -186,7 +182,6 @@ public HttpResponse sendBulkRequest(@NonNull final Iterable requests, * * @param requests the requests to send * @return future with response from these requests - * @throws IOException thrown if there was a problem with this connection */ public Future sendBulkRequestAsync(@NonNull final Iterable requests) { return sendBulkRequestAsync(requests, null, null); @@ -258,7 +252,6 @@ public Future sendBulkRequestAsync(@NonNull final Iterable sendBulkRequestAsync(@NonNull final Iterable requests, @Nullable FutureCallback callback) { return sendBulkRequestAsync(requests, null, callback); @@ -272,7 +265,6 @@ public Future sendBulkRequestAsync(@NonNull final Iterable sendBulkRequestAsync(@NonNull final Iterable requests, @Nullable final String authToken) { return sendBulkRequestAsync(requests, authToken, null);