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

net.intelie.liverig.witsml.etp.ETPClient Maven / Gradle / Ivy

The newest version!
package net.intelie.liverig.witsml.etp;

import Energistics.Datatypes.ChannelData.ChannelMetadataRecord;
import Energistics.Datatypes.ChannelData.ChannelRangeInfo;
import Energistics.Datatypes.ChannelData.ChannelStreamingInfo;
import Energistics.Datatypes.MessageHeader;
import Energistics.Protocol.ChannelStreaming.*;
import Energistics.Protocol.Core.CloseSession;
import Energistics.Protocol.Core.OpenSession;
import Energistics.Protocol.Discovery.GetResources;
import Energistics.Protocol.Discovery.GetResourcesResponse;
import Energistics.Protocol.Store.GetObject;
import Energistics.Protocol.Store.Object;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import net.intelie.liverig.witsml.deserializers.GsonByteBufferTypeAdapter;
import net.intelie.liverig.witsml.deserializers.GsonCharSequenceTypeAdapter;
import net.intelie.liverig.witsml.etp.processor.ETPMessageProcessor;
import net.intelie.liverig.witsml.etp.processor.ETPMessageProcessorFactory;
import net.intelie.liverig.witsml.objects.LogIndex;
import net.intelie.liverig.witsml.objects.LogRange;
import org.apache.avro.io.BinaryEncoder;
import org.apache.avro.io.EncoderFactory;
import org.apache.avro.specific.SpecificDatumWriter;
import org.apache.avro.specific.SpecificRecord;
import org.glassfish.tyrus.client.ClientManager;
import org.glassfish.tyrus.client.ClientProperties;
import org.glassfish.tyrus.client.auth.Credentials;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.websocket.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

public class ETPClient {
    private static final Logger LOGGER = LoggerFactory.getLogger(ETPClient.class);

    private static final String SUBPROTOCOL = "energistics-tp";


    private final URI uri;
    private final String application;
    private final String version;
    private final String username;
    private final String password;
    private final boolean jsonEnconding;
    private final Gson gson = new GsonBuilder()
            .registerTypeAdapter(CharSequence.class, new GsonCharSequenceTypeAdapter())
            .registerTypeAdapter(ByteBuffer.class, new GsonByteBufferTypeAdapter())
            .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").setPrettyPrinting()
            .create();

    private final ETPMessageFactory etpMessageFactory = new ETPMessageFactory();

    public ETPClient(URI uri, String application, String version, String username, String password, boolean jsonEnconding) {
        this.uri = uri;
        this.application = application;
        this.version = version;
        this.username = username;
        this.password = password;
        this.jsonEnconding = jsonEnconding;
    }

    public Session getSession() {
        final Session etpSession = connect();
        assert (etpSession != null && etpSession.isOpen());
        return etpSession;
    }

    public Session connect() {
        final Session[] etpSession = new Session[1];
        try {
            ClientEndpointConfig cec = ClientEndpointConfig.Builder.create().preferredSubprotocols(Arrays.asList(SUBPROTOCOL)).build();
            ClientManager client = ClientManager.createClient();
            client.getProperties().put(ClientProperties.CREDENTIALS, new Credentials(username, password));
            client.getProperties().put(ClientProperties.LOG_HTTP_UPGRADE, true);
            client.getProperties().put("application", application);
            client.getProperties().put("applicationVersion", version);
            client.setDefaultMaxSessionIdleTimeout(-1);
            client.connectToServer(new Endpoint() {
                @Override
                public void onError(Session session, Throwable thr) {
                    LOGGER.error("[Session {}] Session has a error.", session.getId(), thr);
                }

                @Override
                public void onClose(Session session, CloseReason closeReason) {
                    LOGGER.debug("[Session {}] was closed: {}", session.getId(), closeReason);
                }

                @Override
                public void onOpen(Session session, EndpointConfig config) {
                    assert (session != null && session.isOpen());
                    LOGGER.debug("[Session {}] Session has been opened.", session.getId());
                    etpSession[0] = session;
                }
            }, cec, this.uri);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return (etpSession == null || etpSession.length == 0 ? null : etpSession[0]);
    }

    private void closeWebSocketSession() {
        /*if (etpSession != null && session.isOpen()) {
            try {
                session.close();
            } catch (IOException e) {
                LOGGER.error("Fail to close session", e);
            }
        }*/
    }

    private MessageHandler configureMessageHanlder(StringBuilder logMessage,
                                                   @NotNull ETPMessageProcessor etpMessageProcessor,
                                                   CountDownLatch messageLatch, List specificRecords, Session session) {
        final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        return new MessageHandler.Partial() {
            @Override
            public void onMessage(ByteBuffer buffer, boolean last) {
                try {
                    outputStream.write(buffer.array());
                    if (last) {
                        logMessage.append(new String(outputStream.toByteArray(), StandardCharsets.UTF_8));
                        LOGGER.debug("[Session {}] Received last message.", session.getId());
                        SpecificRecord specificRecord = processMessage(outputStream.toByteArray(), etpMessageProcessor, messageLatch);
                        if (specificRecord != null) {
                            specificRecords.add(specificRecord);
                        }
                        outputStream.reset();
                    } else {
                        LOGGER.debug("[Session {}] Received partial message.", session.getId());
                    }
                } catch (Exception e) {
                    LOGGER.warn("[Session {}] Received message: {} ", session.getId(), logMessage);
                    LOGGER.error("[Session {}] FAIL:", session, e);
                }
            }
        };
    }

    private SpecificRecord processMessage(byte[] bytes, ETPMessageProcessor etpMessageProcessor, CountDownLatch
            messageLatch) throws Exception {
        return etpMessageProcessor.process(messageLatch, bytes);
    }

    public  List sendAndWait(@NotNull MessageHeader header,
                                                                       @NotNull T body, Session session, List specificRecords, CountDownLatch messageLatch) throws Exception {
        StringBuilder logMessage = new StringBuilder();

        ETPMessageProcessor etpMessageProcessor = ETPMessageProcessorFactory.getInstance(header);

        session.getMessageHandlers().stream().forEach(messageHandler1 -> session.removeMessageHandler(messageHandler1));

        session.addMessageHandler(configureMessageHanlder(logMessage, etpMessageProcessor, messageLatch, specificRecords, session));

        try {
            if (jsonEnconding) {

                String message = gson.toJson(Arrays.asList(header, body));
                LOGGER.debug("[Session {}] Sending message: {} ", session.getId(), message);
                session.getBasicRemote().sendText(message);
            } else {

                try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
                    // create avro binary encoder to write to memory stream
                    BinaryEncoder encoder = EncoderFactory.get().binaryEncoder(baos, null);
                    //org.apache.avro.

                    // serialize header
                    SpecificDatumWriter headerWriter = new SpecificDatumWriter(header.getSchema());
                    headerWriter.write(header, encoder);

                    // serialize body
                    SpecificDatumWriter bodyWriter = new SpecificDatumWriter(body.getSchema());
                    bodyWriter.write(body, encoder);

                    encoder.flush();

                    byte[] data = baos.toByteArray();
                    session.getBasicRemote().sendBinary(ByteBuffer.wrap(data));
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return specificRecords;

    }

    public  List sendAndWait(@NotNull Energistics.Datatypes.MessageHeader header,
                                                                       @NotNull T body) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        StringBuilder logMessage = new StringBuilder();
        List specificRecords = new ArrayList<>();
        CountDownLatch messageLatch = new CountDownLatch(1);
        ETPMessageProcessor etpMessageProcessor = ETPMessageProcessorFactory.getInstance(header);
        final Session[] etpSession = new Session[1];
        try {

            ClientEndpointConfig cec = ClientEndpointConfig.Builder.create().preferredSubprotocols(Arrays.asList(SUBPROTOCOL)).build();
            ClientManager client = ClientManager.createClient();
            client.getProperties().put(ClientProperties.CREDENTIALS, new Credentials(username, password));
            client.getProperties().put(ClientProperties.LOG_HTTP_UPGRADE, true);
            client.getProperties().put("application", application);
            client.getProperties().put("applicationVersion", version);
            client.connectToServer(new Endpoint() {
                @Override
                public void onError(Session session, Throwable thr) {
                    LOGGER.error("[Session {}] Session has a error.", session.getId(), thr);
                }

                @Override
                public void onClose(Session session, CloseReason closeReason) {
                    LOGGER.debug("[Session {}] was closed: {}", session.getId(), closeReason);
                }

                @Override
                public void onOpen(Session session, EndpointConfig config) {
                    assert (session != null && session.isOpen());
                    LOGGER.debug("[Session {}] Session has been opened.", session.getId());
                    etpSession[0] = session;
                }
            }, cec, this.uri);

            sendAndWait(header, body, etpSession[0], specificRecords, messageLatch);

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        try {
            messageLatch.await(etpMessageProcessor != null ? etpMessageProcessor.getDefaultTimeout() : 10, TimeUnit.SECONDS);

            etpSession[0].close();
        } catch (InterruptedException | IOException e) {
            LOGGER.error("FAIL", e);
        }
        return specificRecords;
    }

    public List discoveryQuery(@NotNull Session session, String uri) {
        try {
            List records = sendAndWait(etpMessageFactory.discoverMessage(), GetResources
                    .newBuilder()
                    .setUri(uri).build());

            List responses = new ArrayList<>();

            for (SpecificRecord specificRecord : records) {
                responses.add((GetResourcesResponse) specificRecord);
            }

            return responses;
        } catch (Exception e) {
            LOGGER.error("FAIL", e);
            throw new RuntimeException(e);
        }
    }


    public List discoveryQuery(String uri) {
        try {
            LOGGER.debug("Discover query: {}", uri);

            List records = sendAndWait(etpMessageFactory.discoverMessage(), GetResources
                    .newBuilder()
                    .setUri(uri).build());

            List responses = new ArrayList<>();

            for (SpecificRecord specificRecord : records) {
                responses.add((GetResourcesResponse) specificRecord);
            }

            return responses;
        } catch (Exception e) {
            LOGGER.error("FAIL", e);
            throw new RuntimeException(e);
        }
    }

    public Object getObjectQuery(String uri) {
        try {
            LOGGER.debug("getObject query: {}", uri);

            List records = sendAndWait(etpMessageFactory.objectMessage(), GetObject
                    .newBuilder()
                    .setUri(uri).build());

            List responses = new ArrayList<>();

            if (!records.isEmpty()) {
                for (SpecificRecord specificRecord : records) {
                    responses.add((Object) specificRecord);
                }
                return responses.get(0);
            } else return null;

        } catch (Exception e) {
            LOGGER.error("FAIL", e);
            throw new RuntimeException(e);
        }
    }

    public ChannelMetadata channelDescribeQuery(List uris) {
        try {
            LOGGER.debug("channelDescribe query: {}", uri);

            List records = sendAndWait(etpMessageFactory.channelDescribeMessage(), ChannelDescribe
                    .newBuilder()
                    .setUris(uris
                            .stream()
                            .map(CharSequence.class::cast)
                            .collect(Collectors.toList())
                    )
                    .build());

            List responses = new ArrayList<>();

            if (!records.isEmpty()) {
                for (SpecificRecord specificRecord : records) {
                    responses.add((ChannelMetadata) specificRecord);
                }
                return responses.get(0);
            } else return null;

        } catch (Exception e) {
            LOGGER.error("FAIL", e);
            throw new RuntimeException(e);
        }
    }

    public void closeSession(@NotNull Session session) {
        try {
            List specificRecords = new ArrayList<>();
            CountDownLatch closeSessionCountDownLatch = new CountDownLatch(1);
            sendAndWait(etpMessageFactory.closeSessionMessage(), CloseSession
                            .newBuilder()
                            .setReason("Session closed")
                            .build(),
                    session, specificRecords, closeSessionCountDownLatch);
            closeSessionCountDownLatch.await(2, TimeUnit.SECONDS);
        } catch (Exception e) {
            LOGGER.error("FAIL to close etp session", e);
            throw new RuntimeException(e);
        } finally {
            if (session != null && session.isOpen()) {
                try {
                    session.close();
                } catch (IOException e) {
                    LOGGER.error("FAIL to close web socket", e);
                }
            }
        }
    }

    public OpenSession requestSession(@NotNull Session session) {
        try {
            List specificRecords = new ArrayList<>();
            CountDownLatch openSessionCountDownLatch = new CountDownLatch(1);

            sendAndWait(etpMessageFactory.requestSessionMessage(), etpMessageFactory.getRequestSession(this.application, this.version), session, specificRecords, openSessionCountDownLatch);
            openSessionCountDownLatch.await();

            List responses = new ArrayList<>();
            if (!specificRecords.isEmpty()) {
                for (SpecificRecord specificRecord : specificRecords) {
                    responses.add((OpenSession) specificRecord);
                }
                return responses.get(0);
            } else return null;

        } catch (Exception e) {
            LOGGER.error("FAIL", e);
            throw new RuntimeException(e);
        }
    }

    public Object getObject(@NotNull Session session, @NotNull String uri) {
        try {
            CountDownLatch countDownLatch = new CountDownLatch(1);
            List specificRecords = new ArrayList<>();

            sendAndWait(etpMessageFactory.objectMessage(), GetObject
                            .newBuilder()
                            .setUri(uri).build(),
                    session, specificRecords, countDownLatch);

            countDownLatch.await(60, TimeUnit.SECONDS);

            List responses = new ArrayList<>();

            if (!specificRecords.isEmpty()) {
                for (SpecificRecord specificRecord : specificRecords) {
                    responses.add((Object) specificRecord);
                }
                return responses.get(0);
            } else return null;

        } catch (Exception e) {
            LOGGER.error("FAIL", e);
            throw new RuntimeException(e);
        }
    }

    public List discovery(@NotNull Session session, @NotNull String uri) {
        try {
            CountDownLatch countDownLatch = new CountDownLatch(1);
            List specificRecords = new ArrayList<>();
            sendAndWait(etpMessageFactory.discoverMessage(), GetResources
                            .newBuilder()
                            .setUri(uri).build(),
                    session, specificRecords, countDownLatch);
            countDownLatch.await(60, TimeUnit.SECONDS);

            List responses = new ArrayList<>();

            for (SpecificRecord specificRecord : specificRecords) {
                responses.add((GetResourcesResponse) specificRecord);
            }

            return responses;
        } catch (Exception e) {
            LOGGER.error("FAIL", e);
            throw new RuntimeException(e);
        }
    }

    public void start(@NotNull Session session) {
        try {
            CountDownLatch startCountDownLatch = new CountDownLatch(1);
            List specificRecords = new ArrayList<>();
            sendAndWait(etpMessageFactory.startMessage(), Start
                            .newBuilder()
                            .setMaxDataItems(1000)
                            .setMaxMessageRate(10000)
                            .build(),
                    session, specificRecords, startCountDownLatch);
            startCountDownLatch.await(5, TimeUnit.SECONDS);
        } catch (Exception e) {
            LOGGER.error("FAIL", e);
            throw new RuntimeException(e);
        }
    }

    public ChannelMetadata describe(@NotNull Session session, List uris) {
        try {
            CountDownLatch channelMetadataCountDownLatch = new CountDownLatch(1);
            List specificRecords = new ArrayList<>();

            sendAndWait(etpMessageFactory.channelDescribeMessage(), ChannelDescribe
                    .newBuilder()
                    .setUris(uris
                            .stream()
                            .map(CharSequence.class::cast)
                            .collect(Collectors.toList())
                    )
                    .build(), session, specificRecords, channelMetadataCountDownLatch);

            channelMetadataCountDownLatch.await(60, TimeUnit.SECONDS);

            List responses = new ArrayList<>();

            if (!specificRecords.isEmpty()) {
                for (SpecificRecord specificRecord : specificRecords) {
                    responses.add((ChannelMetadata) specificRecord);
                }
                return responses.get(0);
            } else return null;

        } catch (Exception e) {
            LOGGER.error("FAIL", e);
            throw new RuntimeException(e);
        }
    }

    public ChannelData startStreaming(@NotNull Session session, List channelStreamingInfos) {
        try {
            CountDownLatch channelDataCountDownLatch = new CountDownLatch(1);
            List specificRecords = new ArrayList<>();

            sendAndWait(etpMessageFactory.channelStreamingStartMessage(), ChannelStreamingStart
                    .newBuilder()
                    .setChannels(channelStreamingInfos)
                    .build(), session, specificRecords, channelDataCountDownLatch);

            channelDataCountDownLatch.await(60, TimeUnit.SECONDS);

            List responses = new ArrayList<>();

            if (!specificRecords.isEmpty()) {
                for (SpecificRecord specificRecord : specificRecords) {
                    responses.add((ChannelData) specificRecord);
                }
                return responses.get(0);
            } else return null;

        } catch (Exception e) {
            LOGGER.error("FAIL", e);
            throw new RuntimeException(e);
        }
    }

    public ChannelData requestRange(@NotNull Session session, @NotNull LogRange range, ChannelMetadata channelMetadata) {
        try {
            CountDownLatch channelDataCountDownLatch = new CountDownLatch(1);
            List specificRecords = new ArrayList<>();

            ChannelRangeInfo.Builder builder = ChannelRangeInfo
                    .newBuilder()
                    .setChannelId(
                            channelMetadata.getChannels().stream()
                                    .map(ChannelMetadataRecord::getChannelId)
                                    .collect(Collectors.toList()));

            if (range instanceof LogIndex) {
                LogIndex logIndex = (LogIndex) range;
                if (logIndex.getStartIndex() != null)
                    builder.setStartIndex(logIndex.getStartIndex().longValue() * 10_000);

                if (logIndex.getEndIndex() != null)
                    builder.setEndIndex(logIndex.getEndIndex().longValue() * 10_000);
            }

            sendAndWait(etpMessageFactory.channelRangeRequestMessage(), ChannelRangeRequest
                    .newBuilder()
                    .setChannelRanges(Arrays.asList(builder.build()))
                    .build(), session, specificRecords, channelDataCountDownLatch);

            channelDataCountDownLatch.await(60, TimeUnit.SECONDS);

            List responses = new ArrayList<>();

            if (!specificRecords.isEmpty()) {
                for (SpecificRecord specificRecord : specificRecords) {
                    responses.add((ChannelData) specificRecord);
                }
                return responses.get(0);
            } else return null;

        } catch (Exception e) {
            LOGGER.error("FAIL", e);
            throw new RuntimeException(e);
        }
    }

    public void stop(@NotNull Session session, List channels) {
        try {
            CountDownLatch stopCountDownLatch = new CountDownLatch(1);
            List specificRecords = new ArrayList<>();
            sendAndWait(etpMessageFactory.channelStreamingStopMessage(), ChannelStreamingStop
                    .newBuilder()
                    .setChannels(channels)
                    .build(), session, specificRecords, stopCountDownLatch);
            stopCountDownLatch.countDown();
        } catch (Exception e) {
            LOGGER.error("FAIL", e);
            throw new RuntimeException(e);
        }
    }
}