Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ mvn clean
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 <strong>dev</strong> branch
* Create a feature branch from the <strong>master</strong> 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 <strong>mvn test</strong>. If everything passes, you may want to make sure that your tests are covering everything you think they are! Run <strong>mvn org.pitest:pitest-maven:mutationCoverage</strong> to find out!
Expand Down
47 changes: 44 additions & 3 deletions src/main/java/org/piwik/java/tracking/PiwikTracker.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
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;
Expand Down Expand Up @@ -112,14 +113,25 @@ public HttpResponse sendRequest(final PiwikRequest request) throws IOException {
* @throws IOException thrown if there was a problem with this connection
*/
public Future<HttpResponse> 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<HttpResponse> sendRequestAsync(final PiwikRequest request, FutureCallback<HttpResponse> 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, null);
return client.execute(get, callback);
} catch (final URISyntaxException e) {
throw new IOException(e);
}
Expand Down Expand Up @@ -148,7 +160,20 @@ public HttpResponse sendBulkRequest(final Iterable<PiwikRequest> requests) throw
* @throws IOException thrown if there was a problem with this connection
*/
public Future<HttpResponse> sendBulkRequestAsync(final Iterable<PiwikRequest> requests) throws IOException {
return sendBulkRequestAsync(requests, null);
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<HttpResponse> sendBulkRequestAsync(final Iterable<PiwikRequest> requests, FutureCallback<HttpResponse> callback) throws IOException {
return sendBulkRequestAsync(requests, null, callback);
}

/**
Expand Down Expand Up @@ -205,6 +230,22 @@ public HttpResponse sendBulkRequest(final Iterable<PiwikRequest> requests, final
* @throws IOException thrown if there was a problem with this connection
*/
public Future<HttpResponse> sendBulkRequestAsync(final Iterable<PiwikRequest> 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<HttpResponse> sendBulkRequestAsync(final Iterable<PiwikRequest> requests, final String authToken,
FutureCallback<HttpResponse> callback) throws IOException {
if (authToken != null && authToken.length() != PiwikRequest.AUTH_TOKEN_LENGTH) {
throw new IllegalArgumentException(authToken + " is not " + PiwikRequest.AUTH_TOKEN_LENGTH + " characters long.");
}
Expand All @@ -230,7 +271,7 @@ public Future<HttpResponse> sendBulkRequestAsync(final Iterable<PiwikRequest> re
post = new HttpPost(uriBuilder.build());
post.setEntity(new StringEntity(ob.build().toString(),
ContentType.APPLICATION_JSON));
return client.execute(post, null);
return client.execute(post, callback);
} catch (final URISyntaxException e) {
throw new IOException(e);
}
Expand Down
63 changes: 57 additions & 6 deletions src/test/java/org/piwik/java/tracking/PiwikTrackerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.util.EntityUtils;
import org.junit.After;
Expand All @@ -30,11 +31,10 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Matchers.argThat;
import static org.mockito.Mockito.doReturn;
Expand Down Expand Up @@ -173,6 +173,57 @@ public void testWithLocalServerAsync() throws Exception {
assertEquals("OK", msgBulk);
}

/**
* Test async API with local server
*/
@Test
public void testWithLocalServerAsyncCallback() throws Exception {
CountDownLatch latch = new CountDownLatch(2);
BlockingQueue<HttpResponse> responses = new LinkedBlockingQueue<>();
BlockingQueue<Exception> exceptions = new LinkedBlockingQueue<>();
AtomicInteger cancelled = new AtomicInteger();

FutureCallback<HttpResponse> cb = new FutureCallback<HttpResponse>() {

@Override
public void completed(HttpResponse httpResponse) {
responses.add(httpResponse);
latch.countDown();
}

@Override
public void failed(Exception e) {
exceptions.add(e);
latch.countDown();
}

@Override
public void cancelled() {
cancelled.incrementAndGet();
latch.countDown();

}
};

// one
PiwikRequest request = new PiwikRequest(3, new URL("http://test.com"));
Future<HttpResponse> respFuture = localTracker.sendRequestAsync(request, cb);
// bulk
List<PiwikRequest> requests = Collections.singletonList(request);
Future<HttpResponse> bulkFuture = localTracker.sendBulkRequestAsync(requests, cb);

assertTrue("Responses not received", latch.await(100, TimeUnit.MILLISECONDS));
assertEquals("Not expecting cancelled responses", 0, cancelled.get());
assertEquals("Not expecting exceptions", exceptions.size(), 0);
assertTrue("Single response future not done", respFuture.isDone());
assertTrue("Bulk response future not done", bulkFuture.isDone());
HttpResponse response = responses.poll(1, TimeUnit.MILLISECONDS);
assertEquals("OK", EntityUtils.toString(response.getEntity()));

HttpResponse bulkResponse = responses.poll(1, TimeUnit.MILLISECONDS);
assertEquals("OK", EntityUtils.toString(bulkResponse.getEntity()));
}

static class CorrectGetRequest implements ArgumentMatcher<HttpGet> {
String url;

Expand Down Expand Up @@ -262,7 +313,7 @@ public void testSendBulkRequestAsync_Iterable() throws Exception {
doReturn(response).when(future).get();
doReturn(true).when(future).isDone();

doReturn(future).when(piwikTracker).sendBulkRequestAsync(requests, null);
doReturn(future).when(piwikTracker).sendBulkRequestAsync(requests);

assertEquals(response, piwikTracker.sendBulkRequestAsync(requests).get());
}
Expand Down Expand Up @@ -304,7 +355,7 @@ public void testSendBulkRequestAsync_Iterable_StringFF() throws Exception {
doReturn(future).when(client)
.execute(argThat(new CorrectPostRequest("{\"requests\":[\"?query\"]}")), any());

assertEquals(response, piwikTracker.sendBulkRequestAsync(requests, null).get());
assertEquals(response, piwikTracker.sendBulkRequestAsync(requests).get());
}

@Test
Expand Down