package com.github.kpavlov.jreactive8583;
import com.github.kpavlov.jreactive8583.netty.pipeline.CompositeIsoMessageHandler;
import com.github.kpavlov.jreactive8583.netty.pipeline.EchoMessageListener;
import com.solab.iso8583.IsoMessage;
import com.solab.iso8583.MessageFactory;
import io.netty.bootstrap.AbstractBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.SocketAddress;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
public abstract class AbstractIso8583Connector<
C extends ConnectorConfiguration,
B extends AbstractBootstrap,
M extends IsoMessage> {
protected final Logger logger = LoggerFactory.getLogger(getClass());
private final CompositeIsoMessageHandler<M> messageHandler;
private final MessageFactory<M> isoMessageFactory;
private final AtomicReference<Channel> channelRef = new AtomicReference<>();
private final C configuration;
private ConnectorConfigurer<C, B> configurer;
private SocketAddress socketAddress;
private EventLoopGroup bossEventLoopGroup;
private EventLoopGroup workerEventLoopGroup;
private B bootstrap;
protected AbstractIso8583Connector(C configuration,
MessageFactory<M> isoMessageFactory,
CompositeIsoMessageHandler<M> messageHandler) {
assert (configuration != null) : "Configuration must be provided";
Objects.requireNonNull(isoMessageFactory, "MessageFactory must be provided");
this.configuration = configuration;
this.isoMessageFactory = isoMessageFactory;
this.messageHandler = messageHandler;
if (configuration.shouldAddEchoMessageListener()) {
messageHandler.addListener(new EchoMessageListener<>(isoMessageFactory));
}
}
// @VisibleForTest
protected AbstractIso8583Connector(C configuration, MessageFactory<M> isoMessageFactory) {
this(configuration, isoMessageFactory, new CompositeIsoMessageHandler<>());
}
public void addMessageListener(IsoMessageListener<M> handler) {
messageHandler.addListener(handler);
}
public void removeMessageListener(IsoMessageListener<M> handler) {
messageHandler.removeListener(handler);
}
/**
* Making connector ready to create a connection / bind to port.
* Creates a Bootstrap
*
* @see AbstractBootstrap
*/
public void init() {
logger.info("Initializing");
bossEventLoopGroup = createBossEventLoopGroup();
workerEventLoopGroup = createWorkerEventLoopGroup();
bootstrap = createBootstrap();
}
public void shutdown() {
if (workerEventLoopGroup != null) {
workerEventLoopGroup.shutdownGracefully();
workerEventLoopGroup = null;
}
if (bossEventLoopGroup != null) {
bossEventLoopGroup.shutdownGracefully();
bossEventLoopGroup = null;
}
}
protected void configureBootstrap(B bootstrap) {
bootstrap.option(ChannelOption.TCP_NODELAY,
Boolean.parseBoolean(System.getProperty(
"nfs.rpc.tcp.nodelay", "true")))
.option(ChannelOption.AUTO_READ, true);
if (configurer != null) {
configurer.configureBootstrap(bootstrap, configuration);
}
}
protected ConnectorConfigurer<C, B> getConfigurer() {
return configurer;
}
public void setConfigurer(ConnectorConfigurer<C, B> connectorConfigurer) {
this.configurer = connectorConfigurer;
}
public SocketAddress getSocketAddress() {
return socketAddress;
}
public void setSocketAddress(SocketAddress socketAddress) {
this.socketAddress = socketAddress;
}
public MessageFactory<M> getIsoMessageFactory() {
return isoMessageFactory;
}
public CompositeIsoMessageHandler<M> getMessageHandler() {
return messageHandler;
}
protected abstract B createBootstrap();
protected B getBootstrap() {
return bootstrap;
}
protected EventLoopGroup createBossEventLoopGroup() {
return new NioEventLoopGroup();
}
protected EventLoopGroup getBossEventLoopGroup() {
return bossEventLoopGroup;
}
protected EventLoopGroup createWorkerEventLoopGroup() {
return new NioEventLoopGroup(Runtime.getRuntime().availableProcessors());
}
protected EventLoopGroup getWorkerEventLoopGroup() {
return workerEventLoopGroup;
}
protected Channel getChannel() {
return channelRef.get();
}
protected void setChannel(Channel channel) {
this.channelRef.set(channel);
}
public C getConfiguration() {
return configuration;
}
}