forked from matomo-org/matomo-java-tracker
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMatomoTracker.java
More file actions
272 lines (250 loc) · 10.1 KB
/
MatomoTracker.java
File metadata and controls
272 lines (250 loc) · 10.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
/*
* 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.NonNull;
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 javax.annotation.Nonnull;
import javax.annotation.Nullable;
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
* <strong>http://your-matomo-domain.tld/matomo.php</strong>. Must not be null
*/
public MatomoTracker(@NonNull 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
* <strong>http://your-matomo-domain.tld/matomo.php</strong>.
* @param timeout the timeout of the sent request in milliseconds
*/
public MatomoTracker(@NonNull final String hostUrl, final int timeout) {
this(hostUrl, null, 0, 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;
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
* <strong>http://your-matomo-domain.tld/matomo.php</strong>.
* @param proxyHost url endpoint for the proxy
* @param proxyPort proxy server port number
*/
public MatomoTracker(@NonNull final String hostUrl, @Nullable final String proxyHost, final int proxyPort) {
this(hostUrl, proxyHost, proxyPort, DEFAULT_TIMEOUT);
}
/**
* Sends a tracking request to Matomo
*
* @param request request to send. must not be null
* @return the response from this request
* @deprecated use sendRequestAsync instead
*/
@Deprecated
public HttpResponse sendRequest(@NonNull final MatomoRequest request) {
final HttpClient client = getHttpClient();
HttpUriRequest get = createGetRequest(request);
log.debug("Sending request via GET: {}", request);
try {
return client.execute(get);
} 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);
}
}
/**
* 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
*/
public Future<HttpResponse> sendRequestAsync(@NonNull final MatomoRequest request) {
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
*/
public Future<HttpResponse> sendRequestAsync(@NonNull final MatomoRequest request, @Nullable FutureCallback<HttpResponse> callback) {
final CloseableHttpAsyncClient client = getHttpAsyncClient();
client.start();
HttpUriRequest get = createGetRequest(request);
log.debug("Sending async request via GET: {}", request);
return client.execute(get, callback);
}
/**
* 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
* @deprecated use sendBulkRequestAsync instead
*/
@Deprecated
public HttpResponse sendBulkRequest(@NonNull final Iterable<? extends MatomoRequest> requests) {
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
* @deprecated use sendBulkRequestAsync instead
*/
@Deprecated
public HttpResponse sendBulkRequest(@NonNull final Iterable<? extends MatomoRequest> 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);
try {
return client.execute(post);
} catch (IOException e) {
throw new MatomoException("Could not send bulk request", e);
}
}
private HttpPost buildPost(@NonNull Iterable<? extends MatomoRequest> requests, @Nullable 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
*/
public Future<HttpResponse> sendBulkRequestAsync(@NonNull final Iterable<? extends MatomoRequest> requests) {
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
*/
public Future<HttpResponse> sendBulkRequestAsync(@NonNull final Iterable<? extends MatomoRequest> requests, @Nullable final String authToken, @Nullable FutureCallback<HttpResponse> callback) {
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
*/
public Future<HttpResponse> sendBulkRequestAsync(@NonNull final Iterable<? extends MatomoRequest> requests, @Nullable FutureCallback<HttpResponse> callback) {
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
*/
public Future<HttpResponse> sendBulkRequestAsync(@NonNull final Iterable<? extends MatomoRequest> requests, @Nullable final String authToken) {
return sendBulkRequestAsync(requests, authToken, null);
}
}