cn.ipokerface.aps.channel.ChannelFactory Maven / Gradle / Ivy
Show all versions of apple-pns-java Show documentation
package cn.ipokerface.aps.channel;
import cn.ipokerface.aps.Constant;
import cn.ipokerface.aps.ObjectFactory;
import cn.ipokerface.aps.auth.ApnsSignKey;
import cn.ipokerface.aps.handler.ApnsClientChannelHandler;
import cn.ipokerface.aps.handler.TokenAuthenticationClientChannelHandler;
import cn.ipokerface.aps.utils.ClientChannelClassUtil;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http2.Http2FrameLogger;
import io.netty.handler.flush.FlushConsolidationHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.resolver.AddressResolverGroup;
import io.netty.util.ReferenceCounted;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.PromiseNotifier;
import java.io.Closeable;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
/**
* Created by PokerFace
* Create Date 2020-08-24.
* Email: [email protected]
* Version 1.0.0
*
* Description:
*/
public class ChannelFactory implements ObjectFactory, Closeable {
private static final long MIN_CONNECT_DELAY_SECONDS = 1;
private static final long MAX_CONNECT_DELAY_SECONDS = 60;
private SslContext sslContext;
private Bootstrap bootstrap;
private EventLoopGroup eventLoopGroup;
private AddressResolverGroup extends SocketAddress> addressResolverGroup;
private Duration connectTimeout;
private InetSocketAddress remoteAddress;
private final AtomicLong currentDelaySeconds = new AtomicLong(0);
private final AtomicBoolean releasedSslContenxt = new AtomicBoolean(false);
public ChannelFactory(InetSocketAddress address,EventLoopGroup eventLoopGroup, SslContext sslContext, ApnsSignKey signKey,
Duration tokenExpiration, Duration connectTimeout, Duration idleInterval,
Http2FrameLogger logger, String defaultTopic) {
this.remoteAddress = address;
this.sslContext = sslContext;
if (this.sslContext instanceof ReferenceCounted) {
((ReferenceCounted) this.sslContext).retain();
}
this.eventLoopGroup = eventLoopGroup;
// this.addressResolverGroup = new RoundRobinDnsAddressResolverGroup(
// ClientChannelClassUtil.getDatagramChannelClass(eventLoopGroup),
// DefaultDnsServerAddressStreamProvider.INSTANCE);
this.bootstrap = new Bootstrap()
.option(ChannelOption.TCP_NODELAY, true)
// .option(ChannelOption.SO_KEEPALIVE, true)
// .resolver(addressResolverGroup)
.remoteAddress(remoteAddress)
.group(eventLoopGroup);
if (connectTimeout != null) {
this.bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, (int) connectTimeout.toMillis());
}
this.bootstrap.handler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel channel) {
final SslHandler sslHandler = ChannelFactory.this.sslContext.newHandler(channel.alloc());
final ApnsClientChannelHandler handler;
final String authority = address.getHostName();
final ApnsClientChannelHandler.ApnsClientChannelHandlerBuilder builder;
if (signKey != null) {
builder = new TokenAuthenticationClientChannelHandler.TokenAuthenticationApnsClientHandlerBuilder()
.signingKey(signKey)
.tokenExpiration(tokenExpiration)
.authority(authority)
.defaultTopic(defaultTopic)
.idleInterval(idleInterval);
} else {
builder = new ApnsClientChannelHandler.ApnsClientChannelHandlerBuilder()
.authority(authority)
.defaultTopic(defaultTopic)
.idleInterval(idleInterval);
}
if (logger != null) {
builder.frameLogger(logger);
}
handler = builder.build();
final ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast(sslHandler);
pipeline.addLast(new FlushConsolidationHandler(FlushConsolidationHandler.DEFAULT_EXPLICIT_FLUSH_AFTER_FLUSHES, true));
pipeline.addLast(new IdleStateHandler(idleInterval.toMillis(), 0, 0, TimeUnit.MILLISECONDS));
pipeline.addLast(handler);
}
});
}
@Override
public Future create(Promise promise) {
final long delay = this.currentDelaySeconds.get();
promise.addListener(future -> {
final long updatedDelay = future.isSuccess() ? 0 :
Math.max(Math.min(delay * 2, MAX_CONNECT_DELAY_SECONDS), MIN_CONNECT_DELAY_SECONDS);
ChannelFactory.this.currentDelaySeconds.compareAndSet(delay, updatedDelay);
});
this.bootstrap.config().group().schedule(() -> {
final Bootstrap bootstrap = ChannelFactory.this.bootstrap.clone()
.channelFactory(new AugmentingReflectiveChannelFactory(
ClientChannelClassUtil.getSocketChannelClass(ChannelFactory.this.bootstrap.config().group()),
Constant.channel_ready_promise_attribute_key, promise));
final ChannelFuture connectFuture = bootstrap.connect();
connectFuture.addListener(future -> {
if (!future.isSuccess()) {
promise.tryFailure(future.cause());
}
});
}, delay, TimeUnit.SECONDS);
return promise;
}
@Override
public Future destroy(Channel channel, Promise promise) {
channel.close().addListener(new PromiseNotifier<>(promise));
return promise;
}
@Override
public void close() {
try {
this.addressResolverGroup.close();
} finally {
if (this.sslContext instanceof ReferenceCounted) {
if (this.releasedSslContenxt.compareAndSet(false, true)) {
((ReferenceCounted) this.sslContext).release();
}
}
}
}
}