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

com.tigerbrokers.stock.openapi.client.https.client.TigerHttpClient Maven / Gradle / Ivy

package com.tigerbrokers.stock.openapi.client.https.client;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.tigerbrokers.stock.openapi.client.TigerApiException;
import com.tigerbrokers.stock.openapi.client.config.ClientConfig;
import com.tigerbrokers.stock.openapi.client.constant.TigerApiConstants;
import com.tigerbrokers.stock.openapi.client.https.domain.ApiModel;
import com.tigerbrokers.stock.openapi.client.https.domain.BatchApiModel;
import com.tigerbrokers.stock.openapi.client.https.domain.trade.model.TradeOrderModel;
import com.tigerbrokers.stock.openapi.client.https.request.TigerCommonRequest;
import com.tigerbrokers.stock.openapi.client.https.request.TigerHttpRequest;
import com.tigerbrokers.stock.openapi.client.https.request.TigerRequest;
import com.tigerbrokers.stock.openapi.client.https.request.user.UserLicenseRequest;
import com.tigerbrokers.stock.openapi.client.https.response.TigerHttpResponse;
import com.tigerbrokers.stock.openapi.client.https.response.TigerResponse;
import com.tigerbrokers.stock.openapi.client.https.response.user.UserLicenseResponse;
import com.tigerbrokers.stock.openapi.client.https.validator.ValidatorManager;
import com.tigerbrokers.stock.openapi.client.struct.enums.AccountType;
import com.tigerbrokers.stock.openapi.client.struct.enums.BizType;
import com.tigerbrokers.stock.openapi.client.struct.enums.Env;
import com.tigerbrokers.stock.openapi.client.struct.enums.License;
import com.tigerbrokers.stock.openapi.client.struct.enums.MethodName;
import com.tigerbrokers.stock.openapi.client.struct.enums.MethodType;
import com.tigerbrokers.stock.openapi.client.struct.enums.TigerApiCode;
import com.tigerbrokers.stock.openapi.client.util.AccountUtil;
import com.tigerbrokers.stock.openapi.client.util.ApiLogger;
import com.tigerbrokers.stock.openapi.client.util.ConfigFileUtil;
import com.tigerbrokers.stock.openapi.client.util.HttpUtils;
import com.tigerbrokers.stock.openapi.client.util.NetworkUtil;
import com.tigerbrokers.stock.openapi.client.util.ReflectionUtil;
import com.tigerbrokers.stock.openapi.client.util.SdkVersionUtils;
import com.tigerbrokers.stock.openapi.client.util.StringUtils;
import com.tigerbrokers.stock.openapi.client.util.TigerSignature;
import com.tigerbrokers.stock.openapi.client.util.builder.AccountParamBuilder;
import java.security.Security;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

import static com.tigerbrokers.stock.openapi.client.constant.TigerApiConstants.ACCESS_TOKEN;
import static com.tigerbrokers.stock.openapi.client.constant.TigerApiConstants.ACCOUNT_TYPE;
import static com.tigerbrokers.stock.openapi.client.constant.TigerApiConstants.BIZ_CONTENT;
import static com.tigerbrokers.stock.openapi.client.constant.TigerApiConstants.CHARSET;
import static com.tigerbrokers.stock.openapi.client.constant.TigerApiConstants.DEVICE_ID;
import static com.tigerbrokers.stock.openapi.client.constant.TigerApiConstants.METHOD;
import static com.tigerbrokers.stock.openapi.client.constant.TigerApiConstants.SDK_VERSION;
import static com.tigerbrokers.stock.openapi.client.constant.TigerApiConstants.SIGN;
import static com.tigerbrokers.stock.openapi.client.constant.TigerApiConstants.SIGN_TYPE;
import static com.tigerbrokers.stock.openapi.client.constant.TigerApiConstants.TIGER_ID;
import static com.tigerbrokers.stock.openapi.client.constant.TigerApiConstants.TIMESTAMP;
import static com.tigerbrokers.stock.openapi.client.constant.TigerApiConstants.TRADE_TOKEN;
import static com.tigerbrokers.stock.openapi.client.constant.TigerApiConstants.VERSION;

public class TigerHttpClient implements TigerClient {

  private String serverUrl;
  private String quoteServerUrl;
  private String paperServerUrl;
  private String tigerId;
  private String privateKey;
  private String tigerPublicKey;
  private String accessToken;
  private String tradeToken;
  private String accountType;
  private String deviceId;
  private int failRetryCounts = TigerApiConstants.DEFAULT_FAIL_RETRY_COUNT;

  private static final String ONLINE_PUBLIC_KEY =
      "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDNF3G8SoEcCZh2rshUbayDgLLrj6rKgzNMxDL2HSnKcB0+GPOsndqSv+a4IBu9+I3fyBp5hkyMMG2+AXugd9pMpy6VxJxlNjhX1MYbNTZJUT4nudki4uh+LMOkIBHOceGNXjgB+cXqmlUnjlqha/HgboeHSnSgpM3dKSJQlIOsDwIDAQAB";

  private static final String SANDBOX_PUBLIC_KEY =
      "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCbm21i11hgAENGd3/f280PSe4g9YGkS3TEXBYMidihTvHHf+tJ0PYD0o3PruI0hl3qhEjHTAxb75T5YD3SGK4IBhHn/Rk6mhqlGgI+bBrBVYaXixmHfRo75RpUUuWACyeqQkZckgR0McxuW9xRMIa2cXZOoL1E4SL4lXKGhKoWbwIDAQAB";

  private String signType = TigerApiConstants.SIGN_TYPE_RSA;
  private String charset = TigerApiConstants.UTF_8;

  private static final long REFRESH_URL_INTERVAL_SECONDS = 300;
  private ScheduledThreadPoolExecutor domainExecutorService;

  static {
    Security.setProperty("jdk.certpath.disabledAlgorithms", "");
  }

  private TigerHttpClient() {
  }

  private static class SingletonInner {
    private static TigerHttpClient singleton = new TigerHttpClient();
  }

  /**
   * get TigerHttpClient instance
   * @return TigerHttpClient
   */
  public static TigerHttpClient getInstance() {
    return TigerHttpClient.SingletonInner.singleton;
  }

  public TigerHttpClient clientConfig(ClientConfig clientConfig) {
    ConfigFileUtil.loadConfigFile(clientConfig);
    init(clientConfig.tigerId, clientConfig.privateKey);
    if (clientConfig.failRetryCounts <= TigerApiConstants.MAX_FAIL_RETRY_COUNT) {
      this.failRetryCounts = Math.max(clientConfig.failRetryCounts, 0);
    }
    TokenManager.getInstance().init(clientConfig);
    initDomainRefreshTask();
    if (clientConfig.isAutoGrabPermission) {
      TigerHttpRequest request = new TigerHttpRequest(MethodName.GRAB_QUOTE_PERMISSION);
      request.setBizContent(AccountParamBuilder.instance().buildJsonWithoutDefaultAccount());
      TigerHttpResponse response = execute(request);
      ApiLogger.info("tigerId:{}, grab_quote_permission:{}, data:{}",
          tigerId, response.getMessage(), response.getData());
    }
    return this;
  }

  private void init(String tigerId, String privateKey) {
    if (tigerId == null) {
      throw new RuntimeException("tigerId is empty.");
    }
    if (privateKey == null) {
      throw new RuntimeException("privateKey is empty.");
    }
    this.tigerId = tigerId;
    this.privateKey = privateKey;
    if (ClientConfig.DEFAULT_CONFIG.getEnv() == Env.PROD) {
      this.tigerPublicKey = ONLINE_PUBLIC_KEY;
    } else {
      this.tigerPublicKey = SANDBOX_PUBLIC_KEY;
    }
    this.deviceId = NetworkUtil.getDeviceId();

    initLicense();
    refreshUrl();
    if (this.serverUrl == null) {
      throw new RuntimeException("serverUrl is empty.");
    }
  }

  public TigerHttpClient accessToken(String accessToken) {
    this.accessToken = accessToken;
    return this;
  }

  private void initLicense() {
    if (null == ClientConfig.DEFAULT_CONFIG.license) {
      try {
        Map urlMap = NetworkUtil.getHttpServerAddress(null, this.serverUrl);
        this.serverUrl = StringUtils.defaultIfEmpty(urlMap.get(BizType.COMMON), this.serverUrl);
        UserLicenseRequest request = UserLicenseRequest.newRequest();
        UserLicenseResponse response = execute(request);
        if (response.isSuccess() && response.getLicenseItem() != null) {
          ApiLogger.debug("license:{}", JSON.toJSONString(response.getLicenseItem(), SerializerFeature.WriteEnumUsingToString));
          ClientConfig.DEFAULT_CONFIG.license = License.valueOf(response.getLicenseItem().getLicense());
        }
      } catch (Exception e) {
        ApiLogger.debug("get license fail. tigerId:{}", tigerId);
      }
    }
  }

  private void initDomainRefreshTask() {
    synchronized (TigerHttpClient.SingletonInner.singleton) {
      if (domainExecutorService == null || domainExecutorService.isTerminated()) {
        domainExecutorService = new ScheduledThreadPoolExecutor(1, new ThreadFactory() {
          @Override
          public Thread newThread(Runnable r) {
            Thread t = Executors.defaultThreadFactory().newThread(r);
            t.setDaemon(true);
            return t;
          }
        });
        domainExecutorService.scheduleWithFixedDelay(
            new Runnable() {
              @Override
              public void run() {
                refreshUrl();
              }
            }, REFRESH_URL_INTERVAL_SECONDS,
            REFRESH_URL_INTERVAL_SECONDS, TimeUnit.SECONDS);
      }
    }
  }

  private void refreshUrl() {
    try {
      Map urlMap = NetworkUtil.getHttpServerAddress(ClientConfig.DEFAULT_CONFIG.license, this.serverUrl);
      String newServerUrl = urlMap.get(BizType.TRADE);
      if (newServerUrl == null) {
        newServerUrl = urlMap.get(BizType.COMMON);
      }
      String newQuoteServerUrl = urlMap.get(BizType.QUOTE) == null ? newServerUrl : urlMap.get(BizType.QUOTE);
      String newPaperServerUrl = urlMap.get(BizType.PAPER) == null ? newServerUrl : urlMap.get(BizType.PAPER);

      this.serverUrl = StringUtils.defaultIfEmpty(newServerUrl, this.serverUrl);
      this.quoteServerUrl = StringUtils.defaultIfEmpty(newQuoteServerUrl, this.quoteServerUrl);
      this.paperServerUrl = StringUtils.defaultIfEmpty(newPaperServerUrl, this.paperServerUrl);
    } catch (Throwable t) {
      ApiLogger.error("refresh serverUrl error", t);
    }
  }

  public String getAccessToken() {
    return accessToken;
  }

  public void setAccessToken(String accessToken) {
    this.accessToken = accessToken;
  }

  public void setTradeToken(String tradeToken) {
    this.tradeToken = tradeToken;
  }

  public String getTradeToken() {
    return tradeToken;
  }

  public void setAccountType(AccountType accountType) {
    if (accountType != null) {
      this.accountType = accountType.name();
    }
  }

  public String getAccountType() {
    return accountType;
  }

  @Override
  public  T execute(TigerRequest request) {
    T response;
    String param = null;
    String data = null;
    try {
      validate(request);
      // after successful verification(string enumeration values may be reset), generate JSON data
      param = JSONObject.toJSONString(buildParams(request), SerializerFeature.WriteEnumUsingToString);
      ApiLogger.debug("request param:{}", param);

      data = HttpUtils.post(getServerUrl(request), param,
          MethodName.PLACE_ORDER == request.getApiMethodName() ? 0 : failRetryCounts);

      ApiLogger.debug("response result:{}", data);
      if (StringUtils.isEmpty(data)) {
        throw new TigerApiException(TigerApiCode.EMPTY_DATA_ERROR);
      }
      response = JSON.parseObject(data, request.getResponseClass());
      if (StringUtils.isEmpty(this.tigerPublicKey) || response.getSign() == null) {
        return response;
      }
      boolean signSuccess =
          TigerSignature.rsaCheckContent(request.getTimestamp(), response.getSign(), this.tigerPublicKey, this.charset);

      if (!signSuccess) {
        throw new TigerApiException(TigerApiCode.SIGN_CHECK_FAILED);
      }
      return response;
    } catch (RuntimeException e) {
      ApiLogger.error("request fail. tigerId:{}, method:{}, param:{}, response:{}",
          tigerId, request == null ? null : request.getApiMethodName(), param, data, e);
      return errorResponse(tigerId, request, e);
    } catch (TigerApiException e) {
      ApiLogger.error("request fail. tigerId:{}, method:{}, param:{}, response:{}",
          tigerId, request == null ? null : request.getApiMethodName(), param, data, e);
      return errorResponse(tigerId, request, e);
    } catch (Exception e) {
      ApiLogger.error("request fail. tigerId:{}, method:{}, param:{}, response:{}",
          tigerId, request == null ? null : request.getApiMethodName(), param, data, e);
      return errorResponse(tigerId, request, e);
    }
  }

  private  T errorResponse(String tigerId, TigerRequest request, TigerApiException e) {
    try {
      T response = request.getResponseClass().newInstance();
      response.setCode(e.getErrCode());
      response.setMessage(e.getErrMsg());
      return response;
    } catch (Exception e1) {
      ApiLogger.error(tigerId, request.getApiMethodName(), request.getApiVersion(), e1);
      return null;
    }
  }

  private  T errorResponse(String tigerId, TigerRequest request, Exception e) {
    try {
      T response = request.getResponseClass().newInstance();
      response.setCode(TigerApiCode.CLIENT_API_ERROR.getCode());
      response.setMessage(TigerApiCode.CLIENT_API_ERROR.getMessage() + "(" + e.getMessage() + ")");
      return response;
    } catch (Exception e1) {
      ApiLogger.error(tigerId, request.getApiMethodName(), request.getApiVersion(), e1);
      return null;
    }
  }

  private Map buildParams(TigerRequest request) {
    Map params = new HashMap<>();
    params.put(METHOD, request.getApiMethodName().getValue());
    params.put(VERSION, request.getApiVersion());
    params.put(SDK_VERSION, SdkVersionUtils.getSdkVersion());
    if (request instanceof TigerHttpRequest) {
      params.put(BIZ_CONTENT, ((TigerHttpRequest) request).getBizContent());
    } else if (request.getApiModel() == null && request instanceof TigerCommonRequest) {
      params.put(BIZ_CONTENT, ((TigerCommonRequest) request).getBizContent());
    } else {
      ApiModel apiModel = request.getApiModel();
      if (apiModel instanceof BatchApiModel) {
        params.put(BIZ_CONTENT, JSONObject.toJSONString(((BatchApiModel) apiModel).getItems(), SerializerFeature.WriteEnumUsingToString));
      } else {
        setDefaultSecretKey(apiModel, request.getApiMethodName());
        params.put(BIZ_CONTENT, JSONObject.toJSONString(apiModel, SerializerFeature.WriteEnumUsingToString));
      }
    }
    params.put(TIMESTAMP, request.getTimestamp());
    params.put(CHARSET, this.charset);
    params.put(TIGER_ID, this.tigerId);
    params.put(SIGN_TYPE, this.signType);
    if (this.accessToken != null) {
      params.put(ACCESS_TOKEN, this.accessToken);
    }
    if (this.tradeToken != null) {
      params.put(TRADE_TOKEN, this.tradeToken);
    }
    if (this.accountType != null) {
      params.put(ACCOUNT_TYPE, this.accountType);
    }
    if (this.deviceId != null) {
      params.put(DEVICE_ID, this.deviceId);
    }
    if (this.tigerId != null) {
      String content = TigerSignature.getSignContent(params);
      params.put(SIGN, TigerSignature.rsaSign(content, privateKey, charset));
    }

    return params;
  }

  private void setDefaultSecretKey(ApiModel apiModel, MethodName methodName) {
    if (methodName != null && methodName.getType() == MethodType.TRADE
        && !StringUtils.isEmpty(apiModel.getAccount())
        && !StringUtils.isEmpty(ClientConfig.DEFAULT_CONFIG.secretKey)) {
      // set default secretKey
      ReflectionUtil.checkAndSetDefaultValue(apiModel, "secretKey", "setSecretKey", ClientConfig.DEFAULT_CONFIG.secretKey);
    }
  }

  /**
   * validate parameters
   * @param request
   * @throws TigerApiException
   */
  private void validate(TigerRequest request) throws TigerApiException {
    if (request instanceof TigerHttpRequest) {
      return;
    }
    // TigerCommonRequest
    ValidatorManager.getInstance().validate(request.getApiModel());
  }

  private String getServerUrl(TigerRequest request) {
    String url = null;
    MethodType methodType = request.getApiMethodName().getType();
    if (MethodType.QUOTE == methodType) {
      url = this.quoteServerUrl;
    } else if (MethodType.TRADE == methodType && paperServerUrl != null) {
      String account = AccountUtil.parseAccount(request);
      if (AccountUtil.isVirtualAccount(account)) {
        url = this.paperServerUrl;
      }
    }
    return url == null ? this.serverUrl : url;
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy