
com.intellifylearning.request.BlockingRequester Maven / Gradle / Ivy
package com.intellifylearning.request;
import java.io.Closeable;
import java.io.IOException;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.github.rholder.retry.Attempt;
import com.github.rholder.retry.RetryListener;
import com.github.rholder.retry.Retryer;
import com.github.rholder.retry.RetryerBuilder;
import com.github.rholder.retry.StopStrategies;
import com.github.rholder.retry.WaitStrategies;
import com.google.common.base.MoreObjects;
import com.google.common.base.Predicate;
import com.google.common.base.Throwables;
import com.google.common.collect.Iterables;
import com.intellify.api.caliper.impl.BatchEntityData;
import com.intellify.api.caliper.impl.BatchEventData;
import com.intellify.api.caliper.impl.EntityData;
import com.intellify.api.caliper.impl.EventData;
import com.intellify.api.caliper.impl.IntellifyBase;
import com.intellifylearning.Client;
import com.intellifylearning.Constants;
import com.intellifylearning.IntellifyRequestException;
import com.intellifylearning.models.Batch;
import com.intellifylearning.models.BatchIntellifyBase;
public class BlockingRequester implements Closeable {
public static final String entityDataURI = "/v1custom/entitydata/batch";
public static final String eventDataURI = "/v1custom/eventdata/batch";
private static final Logger LOG = LoggerFactory.getLogger(Constants.LOGGER);
private static final ObjectMapper MAPPER = new ObjectMapper();
private final CloseableHttpClient httpClient;
private final Client intellifyClient;
private final Retryer requestRetryer;
public static BlockingRequester create(Client client) {
if (client.getOptions().isEnableTestMode()) {
LOG.info("#### RUNNING IN TEST MODE");
}
int requestTimeout = client.getOptions().getTimeout();
RequestConfig requestConfig = RequestConfig.custom()
.setStaleConnectionCheckEnabled(true)
.setConnectTimeout(requestTimeout)
.setSocketTimeout(requestTimeout)
.setConnectionRequestTimeout(requestTimeout).build();
CloseableHttpClient httpClient = HttpClientBuilder.create()
.setDefaultRequestConfig(requestConfig).build();
Retryer retryer = RetryerBuilder. newBuilder()
.retryIfException()
.retryIfResult(new Predicate() {
@Override
public boolean apply(IntellifyServiceResponse input) {
return input == null || input.getStatus() >= 299;
}
})
.withRetryListener(new RetryListener() {
@Override
public void onRetry(Attempt attempt) {
if (attempt.getAttemptNumber() > 1) {
if (attempt.hasException()) {
LOG.warn("Retrying due to exception, #:{}", attempt.getAttemptNumber(),
attempt.getExceptionCause());
} else if (attempt.getResult() instanceof IntellifyServiceResponse) {
IntellifyServiceResponse response = (IntellifyServiceResponse) attempt.getResult();
try {
LOG.warn("Retrying due to http error, #:{}, status:{}, response:{}",
attempt.getAttemptNumber(),
response.getStatus(),
response.getEntity());
} catch (Exception e) {
LOG.error("Unable to show attempt response", e);
}
}
}
}
})
.withWaitStrategy(WaitStrategies.exponentialWait(30, TimeUnit.SECONDS))
.withStopStrategy(StopStrategies.stopAfterAttempt(3))
.build();
return new BlockingRequester(client, httpClient, retryer);
}
public BlockingRequester(Client intellifyClient,
CloseableHttpClient httpClient,
Retryer retryer) {
this.intellifyClient = intellifyClient;
this.httpClient = httpClient;
this.requestRetryer = retryer;
}
@Deprecated
public void send(Batch batch) {
throw new UnsupportedOperationException("Method not supported");
}
public void sendOneEvent(String json) {
sendBatchEvents("[{\"event\":" + json + "}]");
}
public void sendOneEntity(String json) {
sendBatchEntities("[{\"entity\":" + json + "}]");
}
public void sendBatchEvents(String json) {
try {
sendBatchEvents((ArrayNode) MAPPER.readTree(json));
} catch (Exception e) {
throw Throwables.propagate(e);
}
}
public void sendBatchEntities(String json) {
try {
sendBatchEntities((ArrayNode) MAPPER.readTree(json));
} catch (Exception e) {
throw Throwables.propagate(e);
}
}
public void sendIntellifyBaseBatch(BatchIntellifyBase batch) {
LOG.debug("############ Sending describes/events in batch of size ... {}", batch.getBatch().size());
BatchEntityData batchEntityData = getBatchEntityData(batch);
BatchEventData batchEventData = getBatchEventData(batch);
if (!Iterables.isEmpty(batchEntityData.getEntityData())) {
LOG.debug("Sending {} entity data objects", Iterables.size(batchEntityData.getEntityData()));
ArrayNode entities = MAPPER.valueToTree(batchEntityData.getEntityData());
sendBatchEntities(entities);
} else {
LOG.debug("No entity data objects found in batch...");
}
if (!Iterables.isEmpty(batchEventData.getEventData())) {
LOG.debug("Sending {} events", Iterables.size(batchEventData.getEventData()));
ArrayNode events = MAPPER.valueToTree(batchEventData.getEventData());
sendBatchEvents(events);
} else {
LOG.debug("No event data objects found in batch...");
}
}
public void sendBatchEvents(ArrayNode rawEvents) {
ObjectNode batch = JsonNodeFactory.instance.objectNode();
ArrayNode wrapped = batch.putArray("eventData");
decorate(rawEvents, wrapped, "eventData");
sendBatch(batch, eventDataURI);
}
public void sendBatchEntities(ArrayNode rawEntities) {
ObjectNode batch = JsonNodeFactory.instance.objectNode();
ArrayNode wrapped = batch.putArray("entityData");
decorate(rawEntities, wrapped, "entityData");
sendBatch(batch, entityDataURI);
}
protected void decorate(ArrayNode raw, ArrayNode wrapped, String type) {
for (int i = 0; i < raw.size(); i++) {
if (type.equals("eventData")) {
wrapped.add(wrapEventNode((ObjectNode) raw.get(i)));
} else if (type.equals("entityData")) {
wrapped.add(wrapEntityNode((ObjectNode) raw.get(i)));
}
}
}
private ObjectNode wrapEventNode(ObjectNode data) {
return wrapNode(data, "event", "startedAtTime");
}
private ObjectNode wrapEntityNode(ObjectNode data) {
JsonNode entityJsonNode = data.findPath("entity");
String entityId = null;
String entityType = null;
if (!entityJsonNode.isMissingNode()) {
entityId = entityJsonNode.findPath("@id").textValue();
entityType = entityJsonNode.findPath("@type").textValue();
}
return wrapNode(data, "entity", "dateModified")
.put("entityId", entityId)
.put("type", entityType);
}
private ObjectNode wrapNode(ObjectNode data, String recordField, String tsField) {
ObjectNode wrapper = data.has(recordField) ? data : JsonNodeFactory.instance.objectNode();
if (data.has(recordField)) {
data = (ObjectNode) data.get(recordField);
}
wrapper.put("apiKey", intellifyClient.getApiKey());
wrapper.put("sensorId", intellifyClient.getOptions().getSensorId());
wrapper.put("correlationId", intellifyClient.getOptions().getCorrelationId());
if (data.hasNonNull(tsField) && data.get(tsField).asLong() != 0) {
wrapper.put("timestamp", data.get(tsField).asLong());
} else {
wrapper.put("timestamp", DateTime.now().getMillis());
}
wrapper.put("sensorSendTime", DateTime.now().getMillis());
wrapper.set(recordField, data);
return wrapper;
}
private void sendBatch(final JsonNode json, final String uri) {
try {
requestRetryer.call(new Callable() {
@Override
public IntellifyServiceResponse call() throws Exception {
HttpResponse response = null;
try {
long start = System.currentTimeMillis();
HttpPut put = new HttpPut(intellifyClient.getOptions().getHost() + uri);
put.addHeader("Content-Type", "application/json; charset=utf-8");
ByteArrayEntity baEntity = new ByteArrayEntity(MAPPER.writeValueAsBytes(json));
put.setEntity(baEntity);
if (LOG.isDebugEnabled()) {
LOG.debug("About to send request - {}", put.toString());
LOG.debug("entity in request - {}", json);
LOG.debug("ACTUAL entity in request - {}", baEntity.toString());
}
response = httpClient.execute(put);
long duration = System.currentTimeMillis() - start;
intellifyClient.getStatistics().updateRequestTime(duration);
return new IntellifyServiceResponse(response);
} catch (Exception e) {
LOG.warn("Error sending records", e);
throw e;
} finally {
if (response != null) {
EntityUtils.consumeQuietly(response.getEntity());
}
}
}
});
} catch (Exception e) {
throw new IntellifyRequestException(e);
}
}
private BatchEntityData getBatchEntityData(
BatchIntellifyBase batchIntellifyBase) {
BatchEntityData batchEntityData = new BatchEntityData();
for (IntellifyBase entity : batchIntellifyBase.getBatch()) {
if (entity instanceof EntityData) {
batchEntityData.getEntityData().add((EntityData) entity);
}
}
return batchEntityData;
}
private BatchEventData getBatchEventData(
BatchIntellifyBase batchIntellifyBase) {
BatchEventData batchEventData = new BatchEventData();
for (IntellifyBase event : batchIntellifyBase.getBatch()) {
if (event instanceof EventData) {
batchEventData.getEventData().add((EventData) event);
}
}
return batchEventData;
}
@Override
public void close() {
try {
httpClient.close();
} catch (IOException e) {
LOG.debug("Error closing http client", e);
}
}
public static class IntellifyServiceResponse {
private final int status;
private final String entity;
public IntellifyServiceResponse(HttpResponse response) {
try {
this.status = response.getStatusLine().getStatusCode();
this.entity = EntityUtils.toString(response.getEntity());
} catch (Exception e) {
throw Throwables.propagate(e);
}
}
public String getEntity() {
return entity;
}
public int getStatus() {
return status;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("status", status)
.add("entity", entity)
.toString();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy