NetworkTransaction.java
/*
* This file is part of Waarp Project (named also Waarp or GG).
*
* Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
* tags. See the COPYRIGHT.txt in the distribution for a full listing of
* individual contributors.
*
* All Waarp Project is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* Waarp . If not, see <http://www.gnu.org/licenses/>.
*/
package org.waarp.openr66.proxy.network;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelPipelineException;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import org.waarp.common.crypto.ssl.WaarpSslUtility;
import org.waarp.common.logging.SysErrLogger;
import org.waarp.common.logging.WaarpLogger;
import org.waarp.common.logging.WaarpLoggerFactory;
import org.waarp.common.utility.WaarpNettyUtil;
import org.waarp.common.utility.WaarpShutdownHook;
import org.waarp.common.utility.WaarpSystemUtil;
import org.waarp.openr66.protocol.exception.OpenR66Exception;
import org.waarp.openr66.protocol.exception.OpenR66ProtocolNetworkException;
import org.waarp.openr66.protocol.exception.OpenR66ProtocolNoConnectionException;
import org.waarp.openr66.proxy.network.ssl.NetworkSslServerHandler;
import org.waarp.openr66.proxy.network.ssl.NetworkSslServerInitializerProxy;
import java.net.ConnectException;
import java.net.SocketAddress;
import static org.waarp.openr66.protocol.configuration.Configuration.*;
/**
* This class handles Network Transaction connections
*/
public class NetworkTransaction {
/**
* Internal Logger
*/
private static final WaarpLogger logger =
WaarpLoggerFactory.getLogger(NetworkTransaction.class);
private final Bootstrap clientBootstrap;
private final Bootstrap clientSslBootstrap;
private final ChannelGroup networkChannelGroup;
public NetworkTransaction() {
networkChannelGroup = new DefaultChannelGroup("NetworkChannels",
configuration.getSubTaskGroup()
.next());
final NetworkServerInitializerProxy networkServerInitializerProxy =
new NetworkServerInitializerProxy(false);
clientBootstrap = new Bootstrap();
WaarpNettyUtil.setBootstrap(clientBootstrap,
configuration.getNetworkWorkerGroup(),
(int) configuration.getTimeoutCon(),
configuration.getBlockSize() + 64, false);
clientBootstrap.handler(networkServerInitializerProxy);
clientSslBootstrap = new Bootstrap();
if (configuration.isUseSSL() && configuration.getHostSslId() != null) {
final NetworkSslServerInitializerProxy networkSslServerInitializerProxy =
new NetworkSslServerInitializerProxy(true);
WaarpNettyUtil.setBootstrap(clientSslBootstrap,
configuration.getNetworkWorkerGroup(),
(int) configuration.getTimeoutCon(),
configuration.getBlockSize() + 64, false);
clientSslBootstrap.handler(networkSslServerInitializerProxy);
} else {
if (configuration.isWarnOnStartup()) {
logger.warn("No SSL support configured");
} else {
logger.info("No SSL support configured");
}
}
}
/**
* Create a connection to the specified socketAddress with multiple retries
*
* @param socketAddress
* @param isSSL
*
* @return the Channel
*/
public final Channel createConnectionWithRetry(
final SocketAddress socketAddress, final boolean isSSL) {
Channel channel = null;
OpenR66Exception lastException = null;
for (int i = 0; i < RETRYNB; i++) {
try {
channel = createConnection(socketAddress, isSSL);
break;
} catch (final OpenR66ProtocolNoConnectionException e1) {
lastException = e1;
channel = null;
break;
} catch (final OpenR66ProtocolNetworkException e1) {
// Can retry
lastException = e1;
channel = null;
try {
Thread.sleep(WAITFORNETOP * 5);
} catch (final InterruptedException e) {//NOSONAR
SysErrLogger.FAKE_LOGGER.ignoreLog(e);
break;
}
}
}
if (channel == null) {
if (lastException != null) {
logger.info("Proxy Cannot connect : {}", lastException.getMessage());
} else {
logger.info("Proxy Cannot connect!");
}
} else if (lastException != null) {
logger.debug("Proxy Connection retried since {}",
lastException.getMessage());
} else {
logger.info("Proxy Connected");
}
return channel;
}
/**
* Create a connection to the specified socketAddress
*
* @param socketAddress
* @param isSSL
*
* @return the channel
*
* @throws OpenR66ProtocolNetworkException
* @throws OpenR66ProtocolNoConnectionException
*/
private Channel createConnection(final SocketAddress socketAddress,
final boolean isSSL)
throws OpenR66ProtocolNetworkException,
OpenR66ProtocolNoConnectionException {
Channel channel = null;
boolean ok = false;
// check valid limit on server side only (could be the initiator but not a client)
boolean valid = false;
for (int i = 0; i < RETRYNB * 2; i++) {
if (configuration.getConstraintLimitHandler().checkConstraintsSleep(i)) {
logger.info("Proxy Constraints exceeded: {}", i);
} else {
logger.debug("Proxy Constraints NOT exceeded");
valid = true;
break;
}
}
if (!valid) {
// Limit is locally exceeded
logger.debug("Proxy Overloaded local system");
throw new OpenR66ProtocolNetworkException(
"Proxy Cannot connect to remote server due to local overload");
}
try {
channel = createNewConnection(socketAddress, isSSL);
ok = true;
} finally {
if (!ok) {
if (channel != null) {
if (channel.isOpen()) {
WaarpSslUtility.closingSslChannel(channel);
}
channel = null;
}
}
}
return channel;
}
/**
* @param socketServerAddress
* @param isSSL
*
* @return the channel
*
* @throws OpenR66ProtocolNetworkException
* @throws OpenR66ProtocolNoConnectionException
*/
private Channel createNewConnection(final SocketAddress socketServerAddress,
final boolean isSSL)
throws OpenR66ProtocolNetworkException,
OpenR66ProtocolNoConnectionException {
ChannelFuture channelFuture = null;
for (int i = 0; i < RETRYNB; i++) {
try {
if (isSSL) {
if (configuration.getHostSslId() != null) {
channelFuture = clientSslBootstrap.connect(socketServerAddress);
} else {
throw new OpenR66ProtocolNoConnectionException("No SSL support");
}
} else {
channelFuture = clientBootstrap.connect(socketServerAddress);
}
} catch (final ChannelPipelineException e) {
throw new OpenR66ProtocolNoConnectionException(
"Proxy Cannot connect to remote server due to a channel exception");
}
try {
channelFuture.await(configuration.getTimeoutCon() / 3);
} catch (final InterruptedException e1) {//NOSONAR
SysErrLogger.FAKE_LOGGER.ignoreLog(e1);
}
if (channelFuture.isSuccess()) {
final Channel channel = channelFuture.channel();
if (isSSL && !NetworkSslServerHandler.isSslConnectedChannel(channel)) {
logger.info("Proxy KO CONNECT since SSL handshake is over");
channel.close();
throw new OpenR66ProtocolNoConnectionException(
"Proxy Cannot finish connect to remote server");
}
networkChannelGroup.add(channel);
return channel;
} else {
try {
Thread.sleep(WAITFORNETOP * 2);
} catch (final InterruptedException e) {//NOSONAR
SysErrLogger.FAKE_LOGGER.ignoreLog(e);
}
if (!channelFuture.isDone()) {
throw new OpenR66ProtocolNoConnectionException(
"Proxy Cannot connect to remote server due to interruption");
}
if (channelFuture.cause() instanceof ConnectException) {
logger.debug("Proxy KO CONNECT: {}",
channelFuture.cause().getMessage());
throw new OpenR66ProtocolNoConnectionException(
"Proxy Cannot connect to remote server", channelFuture.cause());
} else {
logger.debug("Proxy KO CONNECT but retry", channelFuture.cause());
}
}
}
final Throwable cause = channelFuture == null? null : channelFuture.cause();
throw new OpenR66ProtocolNetworkException(
"Proxy Cannot connect to remote server", cause);
}
/**
* Close all Network Transaction
*/
public final void closeAll() {
logger.debug("Proxy close All Network Channels");
try {
Thread.sleep(RETRYINMS * 2);
} catch (final InterruptedException e) {//NOSONAR
SysErrLogger.FAKE_LOGGER.ignoreLog(e);
}
if (!configuration.isServer()) {
WaarpShutdownHook.shutdownHook.launchFinalExit();
}
for (final Channel channel : networkChannelGroup) {
WaarpSslUtility.closingSslChannel(channel);
}
WaarpNettyUtil.awaitOrInterrupted(networkChannelGroup.close());
try {
Thread.sleep(WAITFORNETOP);
} catch (final InterruptedException e) {//NOSONAR
SysErrLogger.FAKE_LOGGER.ignoreLog(e);
}
configuration.clientStop();
logger.debug("Proxy Last action before exit");
WaarpSystemUtil.stopLogger(false);
}
/**
* @return The number of Network Channels
*/
public final int getNumberClients() {
return networkChannelGroup.size();
}
}