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

info.bitrich.xchangestream.hitbtc.HitbtcStreamingService Maven / Gradle / Ivy

The newest version!
package info.bitrich.xchangestream.hitbtc;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import info.bitrich.xchangestream.hitbtc.dto.HitbtcWebSocketBaseParams;
import info.bitrich.xchangestream.hitbtc.dto.HitbtcWebSocketSubscriptionMessage;
import info.bitrich.xchangestream.service.netty.JsonNettyStreamingService;
import io.netty.handler.codec.http.websocketx.extensions.WebSocketClientExtensionHandler;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Stream;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.knowm.xchange.hitbtc.v2.dto.HitbtcException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/** Created by Pavel Chertalev on 15.03.2018. */
public class HitbtcStreamingService extends JsonNettyStreamingService {
  private static final Logger LOG = LoggerFactory.getLogger(HitbtcStreamingService.class);

  private static final String JSON_METHOD = "method";
  private static final String JSON_SYMBOL = "symbol";
  private static final String JSON_PARAMS = "params";
  private static final String JSON_RESULT = "result";
  private static final String JSON_ERROR = "error";
  private static final String JSON_ID = "id";

  private static final String OP_SNAPSHOT = "snapshot";
  private static final String OP_UPDATE = "update";

  /** Map request Id to Chanel Name and HitBTC method pair */
  private final Map> requests = new HashMap<>();

  public HitbtcStreamingService(String apiUrl) {
    super(apiUrl, Integer.MAX_VALUE);
  }

  @Override
  protected WebSocketClientExtensionHandler getWebSocketClientExtensionHandler() {
    return null;
  }

  @Override
  protected String getChannelNameFromMessage(JsonNode message) throws IOException {

    if (message.has(JSON_ID)) {
      int requestId = message.get(JSON_ID).asInt();
      if (requests.containsKey(requestId)) {
        return requests.get(requestId).getKey();
      }
    }

    if (message.has(JSON_METHOD)) {
      String method = message.get(JSON_METHOD).asText();
      if (message.has(JSON_PARAMS) && message.get(JSON_PARAMS).has(JSON_SYMBOL)) {
        String symbol = message.get(JSON_PARAMS).get(JSON_SYMBOL).asText();

        return Stream.of(OP_UPDATE, OP_SNAPSHOT)
            .filter(method::startsWith)
            .map(name -> method.substring(name.length()).toLowerCase() + "-" + symbol)
            .findFirst()
            .orElse(method.toLowerCase() + "-" + symbol);
      }
      return method;
    }

    throw new IOException("Channel name can't be evaluated from message");
  }

  @Override
  protected void handleMessage(JsonNode message) {
    if (message.has(JSON_ID)) {
      int requestId = message.get(JSON_ID).asInt();
      if (requests.containsKey(requestId)) {

        String subscriptionMethod = requests.get(requestId).getLeft();

        if (message.has(JSON_ERROR)) {
          try {
            HitbtcException exception = objectMapper.treeToValue(message, HitbtcException.class);
            super.handleError(message, exception);
          } catch (JsonProcessingException e) {
            super.handleError(message, e);
          }
        } else {
          boolean result = message.get(JSON_RESULT).asBoolean();
          LOG.info("HitBTC returned {} as result of '{}' method", result, subscriptionMethod);
        }

        requests.remove(requestId);
        return;

      } else {
        LOG.error("Unknown request ID {}", requestId);
      }
    }

    String channel = getChannel(message);
    if (!channels.containsKey(channel)) {
      LOG.warn("The message has been received from disconnected channel '{}'. Skipped.", channel);
      return;
    }

    super.handleMessage(message);
  }

  @Override
  public String getSubscribeMessage(String channelName, Object... args) throws IOException {
    HitbtcWebSocketSubscriptionMessage subscribeMessage =
        generateSubscribeMessage(channelName, "subscribe");
    requests.put(
        subscribeMessage.getId(), ImmutablePair.of(channelName, subscribeMessage.getMethod()));

    return objectMapper.writeValueAsString(subscribeMessage);
  }

  @Override
  public String getUnsubscribeMessage(String channelName, Object... args) throws IOException {

    HitbtcWebSocketSubscriptionMessage subscribeMessage =
        generateSubscribeMessage(channelName, "unsubscribe");
    requests.put(
        subscribeMessage.getId(), ImmutablePair.of(channelName, subscribeMessage.getMethod()));

    return objectMapper.writeValueAsString(subscribeMessage);
  }

  private HitbtcWebSocketSubscriptionMessage generateSubscribeMessage(
      String channelName, String methodType) throws IOException {

    String[] chanelInfo = channelName.split("-");
    if (chanelInfo.length < 2) {
      throw new IOException(
          methodType
              + " message: channel name must has format - (e.g orderbook-ETHBTC)");
    }

    String method = methodType + StringUtils.capitalize(chanelInfo[0]);
    String symbol = chanelInfo[1];
    int requestId = ThreadLocalRandom.current().nextInt(1, Integer.MAX_VALUE);

    return new HitbtcWebSocketSubscriptionMessage(
        requestId, method, new HitbtcWebSocketBaseParams(symbol));
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy