package com.gustz.dove.cli.api.service.impl;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.gustz.dove.cli.api.app.service.impl.ClientAppService;
import com.gustz.dove.cli.api.app.vo.ClientAppVo;
import com.sinovatech.rd.wcsb.cli.api.service.AsyncHttpCliService;
import com.sinovatech.rd.wcsb.cli.api.service.BaseService;
import com.sinovatech.rd.wcsb.cli.api.service.BaseWebsUrl;
import com.sinovatech.rd.wcsb.cli.api.service.CacheService;
import com.gustz.dove.cli.api.service.conf.WebsUrlParam;
import com.sinovatech.rd.wcsb.cli.api.service.dict.RspCodeDict;
import com.gustz.dove.cli.api.service.util.AppLogStyle;
import com.gustz.dove.cli.api.service.util.JsonMapper;
import com.sinovatech.rd.wcsb.cli.api.service.vo.AbstBaseReq;
import com.sinovatech.rd.wcsb.cli.api.service.vo.AbstBaseRsp;
import com.sinovatech.rd.wcsb.cli.api.service.vo.Attachment;
import com.sinovatech.rd.wcsb.cli.api.service.vo.ErrorBodyRsp;
import com.sinovatech.rd.wcsb.cli.api.service.vo.UploadFileForm;
/**
* TODO: Base client service impl
*
* @author ZHENFENG ZHANG
* @since [ Aug 3, 2015 ]
*/
public abstract class AbstBaseService<REQ extends Serializable> implements BaseService<REQ> {
private String dfPatt = "yyyy-MM-dd";
private static final String FAIL_RSP = "fail";
private Map<String, String> websUrlParamMap;
private String accessToken;
@Autowired
private AsyncHttpCliService asyncHttpCliService;
@Autowired
private CacheService<String, Object> cacheService;
@Autowired
private ClientAppService clientAppService;
/**
* Is check client APP
*/
private boolean isCheckCliApp = true;
/**
* try again number 2
*/
private AtomicInteger tryAgainNum = new AtomicInteger(2);
public void setDfPatt(String dfPatt) {
this.dfPatt = dfPatt;
}
public void setWebsUrlParamMap(Map<String, String> websUrlParamMap) {
this.websUrlParamMap = websUrlParamMap;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public void setCheckCliApp(boolean isCheckCliApp) {
this.isCheckCliApp = isCheckCliApp;
}
/**
* Check client APP
*
* @param websUrl
* @param cliAppCode
* @param reqVo
* @throws Exception
*/
private void checkCliApp(BaseWebsUrl websUrl, final String cliAppCode, final REQ reqVo) throws Exception {
if (!this.isCheckCliApp) {
// is check TODO
return;
}
ClientAppVo vo = new ClientAppVo();
// 客户端APP编码 1.
vo.setCliAppCode(cliAppCode);
// 客户端APP密码 2.
final String cliAppPwd = this.readBeanVal(reqVo, AbstBaseReq.CLI_APP_PWD_FIELD);
vo.setCliAppPwd(cliAppPwd);
// 开发者账号 3.
final String accountCode = this.readBeanVal(reqVo, AbstBaseReq.DEV_AC_CODE_FIELD);
vo.setAccountCode(accountCode);
// 服务编码(请求服务的接口编码)4.
vo.setWebsCodes(websUrl.getWebsCode());
// 客户端IP地址集 5.
final String[] cliIpAddrs = this.readBeanVal(reqVo, AbstBaseReq.CLI_IP_ADDRS_FIELD);
vo.setCliIpAddrsIn(cliIpAddrs);
//
this.clientAppService.checkCliApp(vo);
}
/**
* Post for upload file
*
* @param websUrl
* @param sn
* @param cliAppCode client appCode
* @param reqVo
* @param rspVo
* @return
*/
@Override
public <T> T uploadFile(BaseWebsUrl websUrl, long sn, String cliAppCode, final REQ reqVo, final T rspVo) {
AppLogStyle appLogs = new AppLogStyle(sn + "", cliAppCode);
T _rspVo = null;
try {
this.beginLog(appLogs, websUrl, reqVo, rspVo);
// check client APP 1.
this.checkCliApp(websUrl, cliAppCode, reqVo);
// do 2.
_rspVo = this.getUploadRspVo(appLogs, websUrl, reqVo, rspVo);
} catch (Exception e) {
_rspVo = this.getFailRspVo(rspVo);
appLogs.error("", e);
} finally {
appLogs.end(this.toText(_rspVo));
}
return _rspVo;
}
/**
* Get for download file
*
* @param websUrl
* @param sn
* @param cliAppCode client appCode
* @param reqVo
* @param rspVo
* @return
*/
@Override
public <T> T downloadFile(BaseWebsUrl websUrl, long sn, String cliAppCode, final REQ reqVo, final T rspVo) {
AppLogStyle appLogs = new AppLogStyle(sn + "", cliAppCode);
T _rspVo = null;
try {
this.beginLog(appLogs, websUrl, reqVo, rspVo);
// check client APP 1.
this.checkCliApp(websUrl, cliAppCode, reqVo);
// do 2.
_rspVo = this.getDownloadRspVo(appLogs, websUrl, rspVo);
} catch (Exception e) {
_rspVo = this.getFailRspVo(rspVo);
appLogs.error("", e);
} finally {
appLogs.end(this.toText(_rspVo));
}
return _rspVo;
}
/**
* HTTP post request
*
* @param websUrl
* @param cliAppCode client appCode
* @param reqVo
* @param rspVo
* @return
*/
@Override
public <T> T httpPost(BaseWebsUrl websUrl, String cliAppCode, final REQ reqVo, final T rspVo) {
return httpPost(websUrl, System.currentTimeMillis(), cliAppCode, reqVo, rspVo);
}
/**
* HTTP post request
*
* @param websUrl
* @param sn
* @param cliAppCode client appCode
* @param reqVo
* @param rspVo
* @return
*/
@Override
public <T> T httpPost(BaseWebsUrl websUrl, long sn, String cliAppCode, final REQ reqVo, final T rspVo) {
AppLogStyle appLogs = new AppLogStyle(sn + "", cliAppCode);
T _rspVo = null;
try {
this.beginLog(appLogs, websUrl, reqVo, rspVo);
// check client APP 1.
this.checkCliApp(websUrl, cliAppCode, reqVo);
// do 2.
_rspVo = this.getPostRspVo(appLogs, websUrl, reqVo, rspVo);
} catch (Exception e) {
_rspVo = this.getFailRspVo(rspVo);
appLogs.error("", e);
} finally {
appLogs.end(this.toText(_rspVo));
}
return _rspVo;
}
/**
* HTTP post request
*
* @param websUrl
* @param sn
* @param cliAppCode
* @param content
* @return
*/
@Override
public String httpPost(String websUrl, long sn, String cliAppCode, final String content) {
AppLogStyle appLogs = new AppLogStyle(sn + "", cliAppCode);
String _retVal = FAIL_RSP;
try {
this.beginLog(appLogs, websUrl, content);
// check client APP 1. TODO
// this.checkCliApp(websUrl, cliAppCode, reqVo);
// do 2.
_retVal = asyncHttpCliService.post(websUrl, content);
} catch (Exception e) {
appLogs.error("", e);
} finally {
appLogs.end(_retVal);
}
return _retVal;
}
/**
* HTTP get request
*
* @param websUrl
* @param cliAppCode client appCode
* @param rspVo
* @return
*/
@Override
public <T> T httpGet(BaseWebsUrl websUrl, String cliAppCode, final T rspVo) {
return httpGet(websUrl, System.currentTimeMillis(), cliAppCode, rspVo);
}
/**
* HTTP get request
*
* @param websUrl
* @param sn
* @param cliAppCode client appCode
* @param rspVo
* @return
*/
@Override
public <T> T httpGet(BaseWebsUrl websUrl, long sn, String cliAppCode, final T rspVo) {
AppLogStyle appLogs = new AppLogStyle(sn + "", cliAppCode);
T _rspVo = null;
try {
this.beginLog(appLogs, websUrl, null, rspVo);
// check client APP 1. TODO
// this.checkCliApp(websUrl, cliAppCode, reqVo);
// do 2.
_rspVo = this.getGetRspVo(appLogs, websUrl, rspVo);
} catch (Exception e) {
_rspVo = this.getFailRspVo(rspVo);
appLogs.error("", e);
} finally {
appLogs.end(this.toText(_rspVo));
}
return _rspVo;
}
/**
* Is try again
*
* @param appLogs
* @param rspVo
* @return
*/
private <T> boolean isTryAgain(AppLogStyle appLogs, T rspVo) {
Object _retVal = null;
try {
_retVal = this.readBeanVal(rspVo, AbstBaseRsp.RSP_CODE_FIELD);
} catch (Exception e) {
_retVal = null;
appLogs.error("", e);
}
//
return (_retVal != null && RspCodeDict.J9998X.getName().equals(_retVal + ""));
}
/**
* Print begin log
*
* @param appLogs
* @param url
* @param content
*/
private <T> void beginLog(AppLogStyle appLogs, String url, String content) {
appLogs.begin(String.format("%1$s,URL[ %2$s ].", content, url));
}
/**
* Print begin log
*
* @param appLogs
* @param websUrl
* @param reqVo
* @param rspVo
*/
private <T> void beginLog(AppLogStyle appLogs, final BaseWebsUrl websUrl, REQ reqVo, T rspVo) {
appLogs.begin(String.format("%1$s,URL[ %2$s ]=:%3$s,rspVo=:%4$s .", this.toText(reqVo), websUrl.getText(),
websUrl.getUrl(), rspVo));
}
/**
* Write bean to JSON string
*
* @param bean
* @return
* @throws JsonProcessingException
*/
private String writeValueAsString(Object bean) throws JsonProcessingException {
JsonMapper.setDateFormat(this.dfPatt);
return JsonMapper.writeValueAsString(true, bean);
}
/**
* Read JSON to bean
*
* @param json
* @param typeCls
* @return
* @throws JsonParseException
* @throws JsonMappingException
* @throws IOException
*/
private Object readValue(String json, Class<?> typeCls) throws JsonParseException, JsonMappingException, IOException {
JsonMapper.setDateFormat(this.dfPatt);
return JsonMapper.getObjectMapper().readValue(json, typeCls);
}
private String toText(Object msg) {
return ReflectionToStringBuilder.toString(msg);
}
/**
* Get get response VO
*
* @param appLogs
* @param websUrl
* @param rspVo
* @return
* @throws Exception
*/
private <T> T getGetRspVo(AppLogStyle appLogs, final BaseWebsUrl websUrl, final T rspVo) throws Exception {
T _retVo = null;
try {
String _url = this.fmtReqWebsUrl(websUrl);
// log
appLogs.runtime(_url);
String json = fmtJSON(asyncHttpCliService.get(_url));
// JSON to VO
_retVo = this.getSetRspBodyByJson(appLogs, rspVo, json);
} catch (Exception e) {
throw e;
} finally {
// try again
if (this.isTryAgain(appLogs, _retVo) && tryAgainNum.getAndDecrement() > 0) {
appLogs.runtimeWarn(String.format("try again get request number is %1$s .", tryAgainNum.get()));
try {
Thread.sleep(3000); // 3s
//
this.getGetRspVo(appLogs, websUrl, rspVo);
} catch (InterruptedException e) {
appLogs.error("", e);
throw e;
}
}
}
return _retVo;
}
/**
* Get post response VO
*
* @param appLogs
* @param websUrl
* @param reqVo
* @param rspVo
* @return
* @throws Exception
*/
private <T> T getPostRspVo(AppLogStyle appLogs, final BaseWebsUrl websUrl, final REQ reqVo, final T rspVo) throws Exception {
T _retVo = null;
try {
String _url = this.fmtReqWebsUrl(websUrl);
T _reqBodyVo = this.readBeanVal(reqVo, AbstBaseReq.BODY_FIELD); // get request body JSON
// to JSON
String _param = this.writeValueAsString(_reqBodyVo);
// log
appLogs.runtime(_url + _param);
String json = fmtJSON(asyncHttpCliService.post(_url, _param));
// JSON to VO
_retVo = this.getSetRspBodyByJson(appLogs, rspVo, json);
} catch (Exception e) {
throw e;
} finally {
// try again
if (this.isTryAgain(appLogs, _retVo) && tryAgainNum.getAndDecrement() > 0) {
appLogs.runtimeWarn(String.format("try again post request number is %1$s .", tryAgainNum.get()));
try {
Thread.sleep(3000); // 3s
//
this.getPostRspVo(appLogs, websUrl, reqVo, rspVo);
} catch (InterruptedException e) {
appLogs.error("", e);
throw e;
}
}
}
return _retVo;
}
/**
* Get upload file response VO
*
* @param appLogs
* @param websUrl
* @param reqVo
* @param rspVo
* @return
* @throws Exception
*/
private <T> T getUploadRspVo(AppLogStyle appLogs, final BaseWebsUrl websUrl, final REQ reqVo, final T rspVo) throws Exception {
T _retVo = null;
try {
String _url = this.fmtReqWebsUrl(websUrl);
// get request body
UploadFileForm[] _reqBodyVo = this.readBeanVal(reqVo, AbstBaseReq.BODY_FIELD);
// print log
appLogs.runtime(_url + _reqBodyVo);
//
String json = fmtJSON(asyncHttpCliService.uploadFile(_url, _reqBodyVo));
// JSON to VO
_retVo = this.getSetRspBodyByJson(appLogs, rspVo, json);
} catch (Exception e) {
throw e;
}
return _retVo;
}
/**
* Get download file response VO
*
* @param appLogs
* @param websUrl
* @param rspVo
* @return
* @throws Exception
*/
private <T> T getDownloadRspVo(AppLogStyle appLogs, final BaseWebsUrl websUrl, final T rspVo) throws Exception {
T _retVo = null;
try {
String _url = this.fmtReqWebsUrl(websUrl);
// print log
appLogs.runtime(_url);
//
Attachment _atta = asyncHttpCliService.downloadFile(_url);
_retVo = this.getSetRspBody(rspVo, _atta);
} catch (Exception e) {
throw e;
}
return _retVo;
}
private static String fmtJSON(String json) {
if (json == null || json.isEmpty()) {
json = "{}";
}
return json;
}
/**
* Format web service URL
*
* @param websUrl
* @return
*/
private String fmtReqWebsUrl(final BaseWebsUrl websUrl) {
String _url = websUrl.getUrl();
// replace URL param
if (websUrlParamMap == null) {
websUrlParamMap = new HashMap<String, String>();
}
// put access token
websUrlParamMap.put(WebsUrlParam.ACCESS_TOKEN, this.accessToken);
for (Map.Entry<String, String> _entry : this.websUrlParamMap.entrySet()) {
String _key = _entry.getKey();
String _val = _entry.getValue();
if (StringUtils.isNotBlank(_key) && StringUtils.isNotBlank(_val)) {
_url = _url.replace(_key, _val);
}
}
return _url;
}
/**
* Get fail response VO
*
* @param rspVo
* @return
*/
private <T> T getFailRspVo(T rspVo) {
try {
this.setRspCode(rspVo, RspCodeDict.J9999);
} catch (Exception e) {
throw new Error(e);
}
return rspVo;
}
/**
* Filter response VO
*
* @param appLogs
* @param rspVo
* @param json
* @return
* @throws Exception
*/
@SuppressWarnings("unchecked")
private <T> T filterRspVo(AppLogStyle appLogs, T rspVo, final String json) throws Exception {
if (StringUtils.isBlank(json) || (json.indexOf("]") < 1 ? json.indexOf("}") < 1 ? true : false : false)) {
rspVo = this.getFailRspVo(rspVo);
appLogs.runtimeWarn("JSON string is illegal,response fail vo. \n");
}
if (!json.contains(ErrorBodyRsp.EXT_ERR_CODE_KEY)) {
return rspVo;
}
// exist error
Map<String, Object> _map = JsonMapper.getObjectMapper().readValue(json, HashMap.class);
String _code = _map.get(ErrorBodyRsp.EXT_ERR_CODE_KEY) + "";
if (!ErrorBodyRsp.OK_CODE.equals(_code)) {
// rspCode
this.setRspCode(rspVo, _code, _map.get(ErrorBodyRsp.EXT_ERR_MSG_KEY) + "");
//
appLogs.runtimeWarn(String.format("This is 3rd system response error message. %1$s \n", _map));
}
return rspVo;
}
/**
* Get set response body data
*
* @param appLogs
* @param rspVo
* @param json
* @return
* @throws Exception
*/
@SuppressWarnings("unchecked")
private <T> T getSetRspBodyByJson(AppLogStyle appLogs, T rspVo, final String json) throws Exception {
// filter response VO
rspVo = this.filterRspVo(appLogs, rspVo, json);
// read body VO
T _bodyVo = this.readBeanVal(rspVo, AbstBaseReq.BODY_FIELD);
// set body data
_bodyVo = (T) this.readValue(json, _bodyVo.getClass());
//
return this.getSetRspBody(rspVo, _bodyVo);
}
/**
* Set response code and msg
*
* @param rspVo
* @param codeDict
* @throws Exception
*/
private void setRspCode(Object rspVo, RspCodeDict codeDict) throws Exception {
this.setRspCode(rspVo, codeDict.getName(), codeDict.getValue());
}
/**
* Set response code and msg
*
* @param rspVo
* @param code
* @param text
* @throws Exception
*/
private void setRspCode(Object rspVo, String code, String text) throws Exception {
if (rspVo != null && StringUtils.isNotBlank(code)) {
this.writeBeanVal(rspVo, AbstBaseRsp.RSP_CODE_FIELD, code);
this.writeBeanVal(rspVo, AbstBaseRsp.RSP_MSG_FIELD, text);
}
}
/**
* Get set response body data
*
* @param rspVo
* @param value
* @return
* @throws Exception
*/
private <T> T getSetRspBody(final T rspVo, Object value) throws Exception {
//
this.writeBeanVal(rspVo, AbstBaseReq.BODY_FIELD, value);
// set success flag
Object _retVal = this.readBeanVal(rspVo, AbstBaseRsp.RSP_CODE_FIELD);
if (_retVal == null) {
this.setRspCode(rspVo, RspCodeDict.A0000);
}
return rspVo;
}
/**
* Read bean value
*
* @param bean
* @param fieldName
* @return
* @throws Exception
*/
@SuppressWarnings("unchecked")
private <T> T readBeanVal(Object bean, String fieldName) throws Exception {
if (bean == null || fieldName == null || fieldName.isEmpty()) {
return null;
}
PropertyDescriptor[] descriptors = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();
for (PropertyDescriptor desc : descriptors) {
if (desc.getName().equals(fieldName)) {
return (T) desc.getReadMethod().invoke(bean);
}
}
return null;
}
/**
* Write bean value
*
* @param bean
* @param fieldName
* @param value
* @return
* @throws Exception
*/
private void writeBeanVal(Object bean, String fieldName, Object value) throws Exception {
if (bean == null || fieldName == null || fieldName.isEmpty()) {
throw new IllegalArgumentException("Args 'bean/fieldName' is null.");
}
PropertyDescriptor[] descriptors = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();
for (PropertyDescriptor desc : descriptors) {
if (desc.getName().equals(fieldName)) {
desc.getWriteMethod().invoke(bean, value);
}
}
}
/**
* Create cache
*
* @return
*/
@Override
public CacheService<String, Object> createCache() {
return cacheService;
}
protected abstract void setAccessTokenX(long sn, String cliAppCode, String devAcCode);
}