io.github.yawenok.fcm.client.impl.FcmHttpClient Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of fcm-client Show documentation
Show all versions of fcm-client Show documentation
FCM client for java!Based on HttpAsyncClient.
The newest version!
package io.github.yawenok.fcm.client.impl;
import com.alibaba.fastjson.JSON;
import io.github.yawenok.fcm.client.FcmClient;
import io.github.yawenok.fcm.client.concurrent.FutureCallback;
import io.github.yawenok.fcm.client.response.FcmMessageResponse;
import io.github.yawenok.fcm.client.response.FcmMessageResultInfo;
import org.apache.http.*;
import org.apache.http.auth.AuthSchemeProvider;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.AuthSchemes;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.config.ConnectionConfig;
import org.apache.http.config.Lookup;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.auth.*;
import org.apache.http.impl.client.*;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager;
import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
import org.apache.http.impl.nio.reactor.IOReactorConfig;
import org.apache.http.nio.conn.NoopIOSessionStrategy;
import org.apache.http.nio.conn.SchemeIOSessionStrategy;
import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.github.yawenok.fcm.client.FcmConfig;
import io.github.yawenok.fcm.client.request.FcmMessage;
import io.github.yawenok.fcm.client.exceptions.ConnectionException;
import io.github.yawenok.fcm.client.FcmConstant;
import io.github.yawenok.fcm.client.proxy.ProxyConfig;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.concurrent.*;
@SuppressWarnings("unchecked")
public class FcmHttpClient implements FcmClient {
private final Logger logger = LoggerFactory.getLogger(FcmHttpClient.class);
private FcmConfig fcmConfig;
private CloseableHttpAsyncClient asyncHttpClient;
@Override
public void init(final FcmConfig config) throws ConnectionException {
if (config == null) {
throw new IllegalArgumentException("fcmConfig is null!");
}
fcmConfig = config;
try {
// 配置io线程
IOReactorConfig ioReactorConfig = IOReactorConfig.custom()
.setIoThreadCount(fcmConfig.getIoThreadCount())
.setTcpNoDelay(true)
.setSoReuseAddress(true)
.setSoKeepAlive(true)
.build();
// 设置协议http和https对应的处理socket链接工厂
Registry strategyRegistry = RegistryBuilder
.create()
.register("http", new NoopIOSessionStrategy())
.register("https", new SSLIOSessionStrategy(createIgnoreVerifySSL()))
.build();
// 连接池设置
PoolingNHttpClientConnectionManager connectionManager = new PoolingNHttpClientConnectionManager(new DefaultConnectingIOReactor(ioReactorConfig), strategyRegistry);
connectionManager.setDefaultMaxPerRoute(config.getMaxPerRoute());
connectionManager.setMaxTotal(config.getMaxTotal());
// 连接相关配置
ConnectionConfig defaultConnectionConfig = ConnectionConfig.custom()
.setMalformedInputAction(CodingErrorAction.IGNORE)
.setUnmappableInputAction(CodingErrorAction.IGNORE)
.setCharset(Consts.UTF_8).build();
// 超时设置
RequestConfig defaultRequestConfig = RequestConfig.custom()
.setConnectTimeout(config.getConnectionTimeout())
.setSocketTimeout(config.getSocketTimeout())
.setConnectionRequestTimeout(config.getConnectionRequestTimeout())
.build();
// 验证方式
Lookup authSchemeRegistry = RegistryBuilder
.create()
.register(AuthSchemes.BASIC, new BasicSchemeFactory())
.register(AuthSchemes.DIGEST, new DigestSchemeFactory())
.register(AuthSchemes.NTLM, new NTLMSchemeFactory())
.register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory())
.register(AuthSchemes.KERBEROS, new KerberosSchemeFactory())
.build();
HttpAsyncClientBuilder httpAsyncClientBuilder = HttpAsyncClients.custom()
.setConnectionManager(connectionManager)
.setDefaultRequestConfig(defaultRequestConfig)
.setDefaultConnectionConfig(defaultConnectionConfig)
.setDefaultAuthSchemeRegistry(authSchemeRegistry);
// 设置代理
ProxyConfig proxyConfig = config.getProxyConfig();
if (proxyConfig != null) {
if (proxyConfig.getUsername() != null && proxyConfig.getPassword() != null) {
// Define the Credentials to be used:
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(proxyConfig.getUsername(), proxyConfig.getPassword()));
// Set the Authentication Strategy:
httpAsyncClientBuilder.setProxyAuthenticationStrategy(new ProxyAuthenticationStrategy());
httpAsyncClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
}
httpAsyncClientBuilder.setProxy(new HttpHost(proxyConfig.getHost(), proxyConfig.getPort()));
}
asyncHttpClient = httpAsyncClientBuilder.build();
asyncHttpClient.start();
} catch (Exception e) {
throw new ConnectionException("FcmClient init error!", e);
}
}
@Override
public void destroy() {
if (asyncHttpClient != null) {
try {
asyncHttpClient.close();
asyncHttpClient = null;
} catch (IOException e) {
logger.error("Destroy client error!", e);
}
}
}
@Override
public FcmMessageResponse sendMessageSync(final FcmMessage message) throws InterruptedException, ExecutionException, ConnectionException {
if (asyncHttpClient == null) {
throw new ConnectionException("Client is not init!");
}
Future responseFuture = this.sendMessageAsync(message);
return responseFuture.get();
}
@Override
public Future sendMessageAsync(final FcmMessage message) throws ConnectionException {
if (asyncHttpClient == null) {
throw new ConnectionException("Client is not init!");
}
HttpUriRequest request = buildRequest(message);
Future responseFuture = asyncHttpClient.execute(request, null);
return new ResponseFuture(responseFuture);
}
@Override
public void sendMessageAsync(final FcmMessage message, final FutureCallback callback) throws ConnectionException {
if (asyncHttpClient == null) {
throw new ConnectionException("Client is not init!");
}
HttpUriRequest request = buildRequest(message);
asyncHttpClient.execute(request, new ResponseFutureCallback(message, callback));
return;
}
private HttpUriRequest buildRequest(final FcmMessage message) {
return RequestBuilder.post(FcmConstant.FCM_HOST_PRODUCTION)
.addHeader(HttpHeaders.AUTHORIZATION, String.format("key=%s", fcmConfig.getApiKey()))
.setHeader(HttpHeaders.CONTENT_TYPE, "application/json")
.setEntity(new StringEntity(message.build(), StandardCharsets.UTF_8))
.build();
}
/**
* 忽略自定义证书验证
*
* @return
*/
private SSLContext createIgnoreVerifySSL() {
try {
SSLContext sslContext = SSLContext.getInstance("SSLv3");
X509TrustManager trustManager = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
sslContext.init(null, new TrustManager[]{trustManager}, null);
return sslContext;
} catch (Exception e) {
logger.error("Ignore verify SSL error!", e);
}
return null;
}
private FcmMessageResponse parseFCMMessageResponse(HttpResponse httpResponse) throws InterruptedException, ExecutionException {
FcmMessageResponse fcmMessageResponse = new FcmMessageResponse();
fcmMessageResponse.setCode(httpResponse.getStatusLine().getStatusCode());
try {
HttpEntity entity = httpResponse.getEntity();
if (entity != null) {
String responseBody = EntityUtils.toString(entity);
if (fcmMessageResponse.getCode() == HttpStatus.SC_OK) {
fcmMessageResponse.setFcmMessageResultInfo(JSON.parseObject(responseBody, FcmMessageResultInfo.class));
}
}
} catch (IOException e) {
logger.error("Parse entity error!", e);
}
return fcmMessageResponse;
}
private final class ResponseFutureCallback implements org.apache.http.concurrent.FutureCallback {
private final FcmMessage message;
private final FutureCallback futureCallback;
private ResponseFutureCallback(FcmMessage message, FutureCallback futureCallback) {
this.message = message;
this.futureCallback = futureCallback;
}
@Override
public void completed(HttpResponse httpResponse) {
if (futureCallback != null) {
try {
FcmMessageResponse fcmMessageResponse = parseFCMMessageResponse(httpResponse);
futureCallback.completed(message, fcmMessageResponse);
} catch (Exception e) {
futureCallback.failed(message, e);
}
}
}
@Override
public void failed(Exception e) {
if (futureCallback != null) {
futureCallback.failed(message, e);
}
}
@Override
public void cancelled() {
if (futureCallback != null) {
futureCallback.cancelled(message);
}
}
}
private final class ResponseFuture implements Future {
private final Future httpResponseFuture;
private FcmMessageResponse fcmMessageResponse = null;
public ResponseFuture(Future httpResponseFuture) {
this.httpResponseFuture = httpResponseFuture;
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return httpResponseFuture.cancel(mayInterruptIfRunning);
}
@Override
public boolean isCancelled() {
return httpResponseFuture.isCancelled();
}
@Override
public boolean isDone() {
return httpResponseFuture.isDone();
}
@Override
public FcmMessageResponse get() throws InterruptedException, ExecutionException {
if (fcmMessageResponse == null) {
fcmMessageResponse = parseFCMMessageResponse(httpResponseFuture.get());
}
return fcmMessageResponse;
}
@Override
public FcmMessageResponse get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
if (fcmMessageResponse == null) {
fcmMessageResponse = parseFCMMessageResponse(httpResponseFuture.get(timeout, unit));
}
return fcmMessageResponse;
}
}
}