package com.skyecho.product.air.ibe.core.util;
import cn.hutool.core.util.StrUtil;
import com.skyecho.product.air.ibe.api.model.IbeExceptionEnum;
import com.skyecho.product.air.ibe.api.model.Response;
import com.skyecho.product.air.ibe.config.properties.IbeProperties;
import com.skyecho.product.kernel.model.exception.ApiException;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.*;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.EntityBuilder;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.*;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeaderElementIterator;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Component;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import javax.annotation.Resource;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.Source;
import javax.xml.transform.sax.SAXSource;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static org.apache.http.Consts.UTF_8;
/**
* Http客户端
*
* @version 1.0
* @date 2019/7/30 12:12
*/
@Slf4j
@Component
public class IbeHttpClient {
// 缓存HttpClient
private static Map<String, CloseableHttpClient> httpClientMap = new ConcurrentHashMap<>();
private String username;
private String password;
/**
* 使用默认配置账号
* @date 2019/7/30 16:56
*/
public <T> Response<T> post(String url,Object request, Class<T> dataClass) {
return post(getCloseableHttpClient(username, password), url,request, dataClass);
}
/**
* 指定账号调用接口
* @date 2019/7/30 16:56
*/
public <T> Response<T> post(String userName, String password, String url,Object request, Class<T> dataClass) {
if (StrUtil.isEmpty(userName) || StrUtil.isEmpty(password)) {
return Response.error("接口账号或密码为空!!");
}
return post(getCloseableHttpClient(userName, password), url,request, dataClass);
}
/**
* @param httpClient HTTP客户端
* @param url 接口地址
* @param dataClass 接口响应数据类型
* @date 2019/7/30 16:57
*/
private <T> Response<T> post(HttpClient httpClient, String url,Object request, Class<T> dataClass) {
// POST访问
String reqXml = beanToXml(request);
HttpPost httpPost = createHttpPost(url,reqXml);
int statusCode = -1;
String body = null;
try {
// 调用接口
HttpResponse httpResponse = httpClient.execute(httpPost);
// 获取请求状态
statusCode = httpResponse.getStatusLine().getStatusCode();
// Read the response body
body = EntityUtils.toString(httpResponse.getEntity(), UTF_8);
EntityUtils.consume(httpResponse.getEntity());
} catch (IOException e) {
log.error("关闭流异常:" + e.getMessage(), e);
throw new Exception("error");
} finally {
log.info("接口地址:{}", httpPost.getURI().toString());
log.info("响应状态:{}", statusCode);
log.info("响应结果:{}", body);
}
// 失败抛出异常
if (statusCode != HttpStatus.SC_OK) {
log.error("Invoke Get Method Failed, HttpStatus = " + statusCode + ";body = " + body);
throw new Exception("error");
}
// 无数据
if (StrUtil.isEmpty(body) || body.contains("<NoData/>")) {
return Response.success();
}
// 不用反序列化
if (dataClass == null) {
return Response.success(null, body);
}
// 有数据
return Response.success(xmlToBean(body, dataClass), body);
}
/**
* 获取HttpClient
* <p>
*
* @param userName 账号
* @param password 密码
* @date 2019/7/30 16:56
*/
private CloseableHttpClient getCloseableHttpClient(String userName, String password) {
CloseableHttpClient httpClient = httpClientMap.get(userName + "_" + password);
if (httpClient == null) {
// 生成客户端
// 凭证
CredentialsProvider credsProvider = new BasicCredentialsProvider();
// 用户名密码验证
credsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(userName, password));
// 生成客户端
httpClient = getHttpClientBuilder().setDefaultCredentialsProvider(credsProvider).build();
// 缓存
httpClientMap.put(userName + "_" + password, httpClient);
}
return httpClient;
}
/**
* HttpClient 生成器
*
* @author DuanTao
* @date 2019/8/14 10:08
*/
private HttpClientBuilder getHttpClientBuilder() {
HttpClientBuilder httpClientBuilder = HttpClients.custom();
// 连接池管理
PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
// 设置最大连接数
connManager.setMaxTotal(10);
// 设置每个连接的路由数
connManager.setDefaultMaxPerRoute(10);
// 创建Http请求配置参数
RequestConfig requestConfig = RequestConfig.custom()
// 获取连接超时时间
.setConnectionRequestTimeout(50000)
// 请求超时时间
.setConnectTimeout(50000)
// 响应超时时间
.setSocketTimeout(50000)
.build();
//
ConnectionKeepAliveStrategy myStrategy = (response, context) -> {
// Honor 'keep-alive' header
HeaderElementIterator it = new BasicHeaderElementIterator(response.headerIterator(HTTP.CONN_KEEP_ALIVE));
while (it.hasNext()) {
HeaderElement he = it.nextElement();
String value = he.getValue();
if (value != null && he.getName().equalsIgnoreCase("timeout")) {
try {
return Long.parseLong(value) * 1000;
} catch (NumberFormatException ignore) {
}
}
}
return 30 * 1000;
};
// 把请求相关的超时信息设置到连接客户端
httpClientBuilder.setDefaultRequestConfig(requestConfig)
// 把请求重试设置到连接客户端(默认3次)
.setRetryHandler(new DefaultHttpRequestRetryHandler())
// 配置连接池管理对象
.setConnectionManager(connManager)
//设置Keep-Alive
.setKeepAliveStrategy(myStrategy);
return httpClientBuilder;
}
/**
* HTTP Post
*
* @date 2019/7/31 11:16
*/
private HttpPost createHttpPost(String url,String xml) {
HttpPost httpPost = new HttpPost(url);
httpPost.addHeader("Content-Type", "text/html;charset=UTF-8");
httpPost.addHeader("accept-encoding", "gzip");
httpPost.addHeader("content-encoding", "gzip");
// 设置请求参数
httpPost.setEntity(EntityBuilder.create()
.setText(xml)
.setContentType(ContentType.APPLICATION_XHTML_XML)
.gzipCompress()
.build());
return httpPost;
}
/**
* Bean to Xml
* @date 2019/8/1 13:28
*/
private String beanToXml(Object obj) {
StringWriter sw = new StringWriter();
try {
//利用jdk中自带的转换类实现
JAXBContext context = JAXBContext.newInstance(obj.getClass());
Marshaller marshaller = context.createMarshaller();
//格式化xml输出的格式
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.FALSE);
//去掉生成xml的默认报文头
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
//将对象转换成输出流形式的xml
marshaller.marshal(obj, sw);
} catch (JAXBException e) {
log.error("JAXB序化异常:" + e.getMessage(), e);
throw new Exception("JAXB序化异常:" + e.getMessage());
}
return sw.toString();
}
/**
* XML转Bean
* <p>
*
* @param body XML内容
* @param dataClass 数据类型
* @date 2019/7/30 16:37
*/
private <T> T xmlToBean(String body, Class<T> dataClass) throws ApiException {
if (StrUtil.isEmpty(body)) {
return null;
}
T data;
try {
JAXBContext context = JAXBContext.newInstance(dataClass);
Unmarshaller unmarshaller = context.createUnmarshaller();
//解析忽略命名空间
SAXParserFactory sax = SAXParserFactory.newInstance();
sax.setNamespaceAware(false);
XMLReader xmlReader = sax.newSAXParser().getXMLReader();
Source source = new SAXSource(xmlReader, new InputSource(new StringReader(body)));
// 反序列化
data = (T) unmarshaller.unmarshal(source);
} catch (Exception e) {
log.error("JAXB反序化异常:" + e.getMessage(), e);
throw new Exception("JAXB反序化异常:" + e.getMessage());
}
return data;
}
}
package com.skyecho.product.air.ibe.api.model;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 自定义响应类
* @date Sep 3, 2019
*/
@Data
@NoArgsConstructor
public class Response<T> implements Serializable {
/**
* 调用接口是否成功
*/
private boolean success;
/**
* 调用接口失败信息
*/
private String error;
/**
* 接口返回原始数据
*/
private String body;
/**
* 接口响应数据
*/
private T data;
private Response(boolean success, String error, String body, T data) {
this.success = success;
this.error = error;
this.body = body;
this.data = data;
}
/**
* 调用接口成功无数据
*
* @date 2019/7/30 16:29
*/
public static <T> Response<T> success() {
return new Response<>(true, null, null, null);
}
/**
* 调用成功 *
* @date 2019/7/30 16:29
*/
public static <T> Response<T> success(T data, String body) {
return new Response<>(true, null, body, data);
}
/**
* 调用失败 *
* @date 2019/7/30 16:29
*/
public static <T> Response<T> error(String message) {
return new Response<>(false, message, null, null);
}
}