All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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