/**
* This file is part of Waarp Project.
*
* Copyright 2009, Frederic Bregier, 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.protocol.networkhandler;
import static org.waarp.openr66.context.R66FiniteDualStates.AUTHENTR;
import java.net.ConnectException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelId;
import io.netty.channel.ChannelPipelineException;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.local.LocalChannel;
import io.netty.util.Timeout;
import io.netty.util.TimerTask;
import io.netty.util.internal.ConcurrentSet;
import org.waarp.common.crypto.ssl.WaarpSslUtility;
import org.waarp.common.database.DbAdmin;
import org.waarp.common.digest.FilesystemBasedDigest;
import org.waarp.common.future.WaarpLock;
import org.waarp.common.logging.WaarpLogger;
import org.waarp.common.logging.WaarpLoggerFactory;
import org.waarp.common.lru.SynchronizedLruCache;
import org.waarp.common.utility.WaarpNettyUtil;
import org.waarp.common.utility.WaarpThreadFactory;
import org.waarp.openr66.context.ErrorCode;
import org.waarp.openr66.context.R66Result;
import org.waarp.openr66.context.R66Session;
import org.waarp.openr66.context.task.exception.OpenR66RunnerErrorException;
import org.waarp.openr66.database.data.DbHostAuth;
import org.waarp.openr66.protocol.configuration.Configuration;
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.protocol.exception.OpenR66ProtocolNoDataException;
import org.waarp.openr66.protocol.exception.OpenR66ProtocolNoSslException;
import org.waarp.openr66.protocol.exception.OpenR66ProtocolPacketException;
import org.waarp.openr66.protocol.exception.OpenR66ProtocolRemoteShutdownException;
import org.waarp.openr66.protocol.exception.OpenR66ProtocolSystemException;
import org.waarp.openr66.protocol.localhandler.LocalChannelReference;
import org.waarp.openr66.protocol.localhandler.RetrieveRunner;
import org.waarp.openr66.protocol.localhandler.packet.AuthentPacket;
import org.waarp.openr66.protocol.localhandler.packet.ConnectionErrorPacket;
import org.waarp.openr66.protocol.networkhandler.packet.NetworkPacket;
import org.waarp.openr66.protocol.networkhandler.ssl.NetworkSslServerHandler;
import org.waarp.openr66.protocol.networkhandler.ssl.NetworkSslServerInitializer;
import org.waarp.openr66.protocol.utils.ChannelUtils;
import org.waarp.openr66.protocol.utils.R66Future;
import org.waarp.openr66.protocol.utils.R66ShutdownHook;
/**
* This class handles Network Transaction connections
*
* @author frederic bregier
*/
public class NetworkTransaction {
/**
* Internal Logger
*/
private static final WaarpLogger logger = WaarpLoggerFactory.getLogger(NetworkTransaction.class);
/**
* To protect access to socketLocks when no address associated
*/
private static final WaarpLock emptyLock = new WaarpLock();
/**
* Lock for Lock management operations
*/
private static final ReentrantLock lockOfLock = new ReentrantLock();
/**
* Hashmap for lock based on remote address
*/
private static final SynchronizedLruCache<Integer, WaarpLock> reentrantLockOnSocketAddressConcurrentHashMap =
new SynchronizedLruCache<Integer, WaarpLock>(20000, 180000);
/**
* Hashmap for Currently Shutdown remote host based on socketAddress.hashCode()
*/
private static final ConcurrentHashMap<Integer, NetworkChannelReference> networkChannelShutdownOnSocketAddressConcurrentHashMap =
new ConcurrentHashMap<Integer, NetworkChannelReference>();
/**
* Hashmap for Currently blacklisted remote host based on IP address(String).hashCode()
*/
private static final ConcurrentHashMap<Integer, NetworkChannelReference> networkChannelBlacklistedOnInetSocketAddressConcurrentHashMap =
new ConcurrentHashMap<Integer, NetworkChannelReference>();
/**
* Hashmap for currently active remote host based on socketAddress.hashCode()
*/
private static final ConcurrentHashMap<Integer, NetworkChannelReference> networkChannelOnSocketAddressConcurrentHashMap =
new ConcurrentHashMap<Integer, NetworkChannelReference>();
/**
* Remote Client NetworkChannels: used to centralize remote requester hosts (possible different address used)
*/
private static final ConcurrentHashMap<String, ClientNetworkChannels> clientNetworkChannelsPerHostId =
new ConcurrentHashMap<String, ClientNetworkChannels>();
/**
* Hashmap for currently active Retrieve Runner (sender)
*/
private static final ConcurrentHashMap<Integer, RetrieveRunner> retrieveRunnerConcurrentHashMap =
new ConcurrentHashMap<Integer, RetrieveRunner>();
/**
* ExecutorService for RetrieveOperation
*/
private static final ExecutorService retrieveExecutor = Executors
.newCachedThreadPool(new WaarpThreadFactory("RetrieveExecutor"));
private final Bootstrap clientBootstrap;
private final Bootstrap clientSslBootstrap;
private final ChannelGroup networkChannelGroup;
public NetworkTransaction() {
networkChannelGroup = new DefaultChannelGroup("NetworkChannels", Configuration.configuration.getSubTaskGroup()
.next());
NetworkServerInitializer networkServerInitializer = new NetworkServerInitializer(false);
clientBootstrap = new Bootstrap();
WaarpNettyUtil.setBootstrap(clientBootstrap, Configuration.configuration.getNetworkWorkerGroup(),
(int) Configuration.configuration.getTIMEOUTCON());
clientBootstrap.handler(networkServerInitializer);
clientSslBootstrap = new Bootstrap();
if (Configuration.configuration.isUseSSL() && Configuration.configuration.getHOST_SSLID() != null) {
NetworkSslServerInitializer networkSslServerInitializer = new NetworkSslServerInitializer(true);
WaarpNettyUtil.setBootstrap(clientSslBootstrap, Configuration.configuration.getNetworkWorkerGroup(),
(int) Configuration.configuration.getTIMEOUTCON());
clientSslBootstrap.handler(networkSslServerInitializer);
} else {
if (Configuration.configuration.isWarnOnStartup()) {
logger.warn("No SSL support configured");
} else {
logger.info("No SSL support configured");
}
}
}
public static String hashStatus() {
String partial = "NetworkTransaction: [InShutdown: "
+ networkChannelShutdownOnSocketAddressConcurrentHashMap.size() +
" Blacklisted: " + networkChannelBlacklistedOnInetSocketAddressConcurrentHashMap.size() +
"\n RetrieveRunner: " + retrieveRunnerConcurrentHashMap.size() +
" ClientNetworkChannels: " + clientNetworkChannelsPerHostId.size();
int nb = 0;
for (ClientNetworkChannels clientNetworkChannels : clientNetworkChannelsPerHostId.values()) {
nb += clientNetworkChannels.size();
}
partial += " Sum of ClientNetworkChannels NetworkClients: " + nb;
nb = 0;
for (NetworkChannelReference ncr : networkChannelOnSocketAddressConcurrentHashMap.values()) {
nb += ncr.nbLocalChannels();
}
partial += "\n NetworkChannels: " + networkChannelOnSocketAddressConcurrentHashMap.size() +
" LockOnSocketAddress: " + reentrantLockOnSocketAddressConcurrentHashMap.size() +
" Sum of NetworkChannels LocalClients: " + nb + "] ";
return partial;
}
private static final WaarpLock getLockNCR(SocketAddress sa) {
return reentrantLockOnSocketAddressConcurrentHashMap.get(sa.hashCode());
}
private static final void addLockNCR(SocketAddress sa, WaarpLock lock) {
reentrantLockOnSocketAddressConcurrentHashMap.put(sa.hashCode(), lock);
}
private static final void addNCR(NetworkChannelReference ncr) {
networkChannelOnSocketAddressConcurrentHashMap.put(ncr.getSocketHashCode(), ncr);
}
private static final NetworkChannelReference removeNCR(NetworkChannelReference ncr) {
return networkChannelOnSocketAddressConcurrentHashMap.remove(ncr.getSocketHashCode());
}
private static final NetworkChannelReference getNCR(SocketAddress sa) {
return networkChannelOnSocketAddressConcurrentHashMap.get(sa.hashCode());
}
private static final boolean containsNCR(SocketAddress address) {
return networkChannelOnSocketAddressConcurrentHashMap.containsKey(address.hashCode());
}
private static final void addShutdownNCR(NetworkChannelReference ncr) {
networkChannelShutdownOnSocketAddressConcurrentHashMap.put(ncr.getSocketHashCode(), ncr);
}
private static final NetworkChannelReference removeShutdownNCR(NetworkChannelReference ncr) {
return networkChannelShutdownOnSocketAddressConcurrentHashMap.remove(ncr.getSocketHashCode());
}
private static final boolean containsShutdownNCR(NetworkChannelReference ncr) {
return networkChannelShutdownOnSocketAddressConcurrentHashMap.containsKey(ncr.getSocketHashCode());
}
private static final boolean containsShutdownNCR(SocketAddress sa) {
return networkChannelShutdownOnSocketAddressConcurrentHashMap.containsKey(sa.hashCode());
}
private static final NetworkChannelReference getShutdownNCR(SocketAddress sa) {
return networkChannelShutdownOnSocketAddressConcurrentHashMap.get(sa.hashCode());
}
private static final void addBlacklistNCR(NetworkChannelReference ncr) {
networkChannelBlacklistedOnInetSocketAddressConcurrentHashMap.put(ncr.getAddressHashCode(), ncr);
}
private static final NetworkChannelReference removeBlacklistNCR(NetworkChannelReference ncr) {
return networkChannelBlacklistedOnInetSocketAddressConcurrentHashMap.remove(ncr.getAddressHashCode());
}
private static final boolean containsBlacklistNCR(NetworkChannelReference ncr) {
return networkChannelBlacklistedOnInetSocketAddressConcurrentHashMap.containsKey(ncr.getAddressHashCode());
}
private static final boolean containsBlacklistNCR(SocketAddress address) {
return networkChannelBlacklistedOnInetSocketAddressConcurrentHashMap.containsKey(address.hashCode());
}
private static final NetworkChannelReference getBlacklistNCR(SocketAddress sa) {
InetAddress address = ((InetSocketAddress) sa).getAddress();
if (address == null) {
return null;
}
return networkChannelBlacklistedOnInetSocketAddressConcurrentHashMap.get(
address.getHostAddress().hashCode()
);
}
private static final WaarpLock getChannelLock(SocketAddress socketAddress) {
lockOfLock.lock();
try {
if (socketAddress == null) {
// should not
logger.info("SocketAddress empty here !");
return emptyLock;
}
WaarpLock socketLock = getLockNCR(socketAddress);
if (socketLock == null) {
socketLock = new WaarpLock(true);
}
// update TTL
addLockNCR(socketAddress, socketLock);
return socketLock;
} finally {
lockOfLock.unlock();
}
}
private static void removeChannelLock() {
lockOfLock.lock();
try {
reentrantLockOnSocketAddressConcurrentHashMap.forceClearOldest();
} finally {
lockOfLock.unlock();
}
}
/**
* Create a connection to the specified socketAddress with multiple retries
*
* @param socketAddress
* @param isSSL
* @param futureRequest
* @return the LocalChannelReference
*/
public LocalChannelReference createConnectionWithRetry(SocketAddress socketAddress,
boolean isSSL, R66Future futureRequest) {
LocalChannelReference localChannelReference = null;
OpenR66Exception lastException = null;
for (int i = 0; i < Configuration.RETRYNB; i++) {
if (R66ShutdownHook.isShutdownStarting()) {
lastException = new OpenR66ProtocolSystemException("Local system in shutdown");
break;
}
try {
localChannelReference =
createConnection(socketAddress, isSSL, futureRequest);
break;
} catch (OpenR66ProtocolRemoteShutdownException e1) {
lastException = e1;
localChannelReference = null;
break;
} catch (OpenR66ProtocolNoConnectionException e1) {
lastException = e1;
localChannelReference = null;
break;
} catch (OpenR66ProtocolNetworkException e1) {
// Can retry
lastException = e1;
localChannelReference = null;
try {
Thread.sleep(Configuration.configuration.getDelayRetry());
} catch (InterruptedException e) {
break;
}
}
}
if (localChannelReference == null) {
logger.debug("Cannot connect : {}", lastException.getMessage());
} else if (lastException != null) {
logger.debug("Connection retried since {}", lastException.getMessage());
}
return localChannelReference;
}
/**
* Create a connection to the specified socketAddress
*
* @param socketAddress
* @param isSSL
* @param futureRequest
* @return the LocalChannelReference
* @throws OpenR66ProtocolNetworkException
* @throws OpenR66ProtocolRemoteShutdownException
* @throws OpenR66ProtocolNoConnectionException
*/
private LocalChannelReference createConnection(SocketAddress socketAddress, boolean isSSL,
R66Future futureRequest)
throws OpenR66ProtocolNetworkException,
OpenR66ProtocolRemoteShutdownException,
OpenR66ProtocolNoConnectionException {
NetworkChannelReference networkChannelReference = null;
LocalChannelReference localChannelReference = null;
boolean ok = false;
// check valid limit on server side only (could be the initiator but not a client)
DbHostAuth auth = isSSL ? Configuration.configuration.getHOST_SSLAUTH() : Configuration.configuration.getHOST_AUTH();
if (!auth.isClient()) {
boolean valid = false;
for (int i = 0; i < Configuration.RETRYNB * 2; i++) {
if (Configuration.configuration.getConstraintLimitHandler().checkConstraintsSleep(i)) {
logger.debug("Constraints exceeded: " + i);
} else {
logger.debug("Constraints NOT exceeded");
valid = true;
break;
}
}
if (!valid) {
// Limit is locally exceeded
logger.debug("Overloaded local system");
throw new OpenR66ProtocolNetworkException(
"Cannot connect to remote server due to local overload");
}
}
try {
networkChannelReference = createNewConnection(socketAddress, isSSL);
try {
localChannelReference = Configuration.configuration
.getLocalTransaction().createNewClient(networkChannelReference,
ChannelUtils.NOCHANNEL, futureRequest);
} catch (OpenR66ProtocolSystemException e) {
throw new OpenR66ProtocolNetworkException(
"Cannot connect to local channel", e);
} catch (NullPointerException e) {
throw new OpenR66ProtocolNetworkException(
"Cannot connect to local channel", e);
}
ok = true;
} finally {
if (!ok) {
if (networkChannelReference != null) {
checkClosingNetworkChannel(networkChannelReference, null);
}
}
}
if (localChannelReference.getFutureValidateStartup().isDone() &&
localChannelReference.getFutureValidateStartup().isSuccess()) {
sendValidationConnection(localChannelReference);
} else {
OpenR66ProtocolNetworkException exc =
new OpenR66ProtocolNetworkException("Startup is invalid");
logger.debug("Startup is Invalid", exc);
R66Result finalValue = new R66Result(
exc, null, true, ErrorCode.ConnectionImpossible, null);
localChannelReference.invalidateRequest(finalValue);
localChannelReference.getLocalChannel().close();
throw exc;
}
return localChannelReference;
}
/**
*
* @param socketServerAddress
* @param isSSL
* @return the NetworkChannelReference
* @throws OpenR66ProtocolNetworkException
* @throws OpenR66ProtocolRemoteShutdownException
* @throws OpenR66ProtocolNoConnectionException
*/
private NetworkChannelReference createNewConnection(SocketAddress socketServerAddress, boolean isSSL)
throws OpenR66ProtocolNetworkException,
OpenR66ProtocolRemoteShutdownException,
OpenR66ProtocolNoConnectionException {
WaarpLock socketLock = getChannelLock(socketServerAddress);
NetworkChannelReference networkChannelReference;
socketLock.lock();
try {
try {
networkChannelReference = getRemoteChannel(socketServerAddress);
} catch (OpenR66ProtocolNoDataException e1) {
networkChannelReference = null;
}
if (networkChannelReference != null) {
networkChannelReference.use();
logger.info("Already Connected: {}", networkChannelReference);
return networkChannelReference;
}
logger.debug("NEW PHYSICAL CONNECTION REQUIRED");
ChannelFuture channelFuture = null;
for (int i = 0; i < Configuration.RETRYNB; i++) {
if (R66ShutdownHook.isShutdownStarting()) {
throw new OpenR66ProtocolNoConnectionException("Local system in shutdown");
}
try {
if (isSSL) {
if (Configuration.configuration.getHOST_SSLID() != null) {
channelFuture = clientSslBootstrap.connect(socketServerAddress);
} else {
throw new OpenR66ProtocolNoConnectionException("No SSL support");
}
} else {
channelFuture = clientBootstrap.connect(socketServerAddress);
}
} catch (ChannelPipelineException e) {
throw new OpenR66ProtocolNoConnectionException(
"Cannot connect to remote server due to a channel exception");
}
try {
channelFuture.await(Configuration.configuration.getTIMEOUTCON() / 3);
} catch (InterruptedException e1) {
}
if (channelFuture.isSuccess()) {
final Channel channel = channelFuture.channel();
if (isSSL) {
if (!NetworkSslServerHandler.isSslConnectedChannel(channel)) {
logger.debug("KO CONNECT since SSL handshake is over");
channel.close();
throw new OpenR66ProtocolNoConnectionException(
"Cannot finish connect to remote server");
}
}
networkChannelGroup.add(channel);
networkChannelReference = new NetworkChannelReference(channel, socketLock);
addNCR(networkChannelReference);
return networkChannelReference;
} else {
try {
Thread.sleep(Configuration.RETRYINMS);
} catch (InterruptedException e) {
}
if (!channelFuture.isDone()) {
throw new OpenR66ProtocolNoConnectionException(
"Cannot connect to remote server due to interruption");
}
if (channelFuture.cause() instanceof ConnectException) {
logger.debug("KO CONNECT:" +
channelFuture.cause().getMessage());
throw new OpenR66ProtocolNoConnectionException(
"Cannot connect to remote server", channelFuture
.cause());
} else {
logger.debug("KO CONNECT but retry", channelFuture
.cause());
}
}
}
throw new OpenR66ProtocolNetworkException(
"Cannot connect to remote server", channelFuture.cause());
} finally {
socketLock.unlock();
}
}
/**
* Create the LocalChannelReference when a remote local channel starts its connection
*
* @param networkChannelReference
* @param packet
*/
public static void createConnectionFromNetworkChannelStartup(
NetworkChannelReference networkChannelReference,
NetworkPacket packet) {
CreateConnectionFromNetworkChannel ccfnc =
new CreateConnectionFromNetworkChannel(networkChannelReference, packet);
ccfnc.setDaemon(true);
Configuration.configuration.getExecutorService().execute(ccfnc);
}
private static class CreateConnectionFromNetworkChannel extends Thread {
final NetworkChannelReference networkChannelReference;
final NetworkPacket startupPacket;
private CreateConnectionFromNetworkChannel(NetworkChannelReference networkChannelReference,
NetworkPacket packet) {
this.networkChannelReference = networkChannelReference;
this.startupPacket = packet;
}
@Override
public void run() {
setName("Connect_" + startupPacket.getRemoteId());
Channel channel = networkChannelReference.channel();
LocalChannelReference lcr = null;
try {
lcr = Configuration.configuration
.getLocalTransaction().createNewClient(networkChannelReference,
startupPacket.getRemoteId(), null);
} catch (OpenR66ProtocolSystemException e1) {
logger.error("Cannot create LocalChannel for: " + startupPacket + " due to "
+ e1.getMessage());
final ConnectionErrorPacket error = new ConnectionErrorPacket(
"Cannot connect to localChannel since cannot create it", null);
NetworkServerHandler
.writeError(channel, startupPacket.getRemoteId(), startupPacket.getLocalId(), error);
NetworkTransaction.checkClosingNetworkChannel(this.networkChannelReference, null);
startupPacket.clear();
return;
} catch (OpenR66ProtocolRemoteShutdownException e1) {
logger.info("Will Close Local from Network Channel");
WaarpSslUtility.closingSslChannel(channel);
startupPacket.clear();
return;
} catch (OpenR66ProtocolNoConnectionException e1) {
logger.error("Cannot create LocalChannel for: " + startupPacket + " due to "
+ e1.getMessage());
final ConnectionErrorPacket error = new ConnectionErrorPacket(
"Cannot connect to localChannel since cannot create it", null);
NetworkServerHandler
.writeError(channel, startupPacket.getRemoteId(), startupPacket.getLocalId(), error);
NetworkTransaction.checkClosingNetworkChannel(this.networkChannelReference, null);
startupPacket.clear();
return;
}
ByteBuf buf = startupPacket.getBuffer();
lcr.getLocalChannel().writeAndFlush(buf);
}
}
/**
* Send a validation of connection with Authentication
*
* @param localChannelReference
* @throws OpenR66ProtocolNetworkException
* @throws OpenR66ProtocolRemoteShutdownException
*/
private void sendValidationConnection(
LocalChannelReference localChannelReference)
throws OpenR66ProtocolNetworkException,
OpenR66ProtocolRemoteShutdownException {
AuthentPacket authent;
try {
DbHostAuth auth = localChannelReference.getNetworkServerHandler().isSsl() ?
Configuration.configuration.getHOST_SSLAUTH() : Configuration.configuration.getHOST_AUTH();
authent = new AuthentPacket(
Configuration.configuration.getHostId(
localChannelReference.getNetworkServerHandler().isSsl()),
FilesystemBasedDigest.passwdCrypt(
auth.getHostkey()),
localChannelReference.getLocalId());
} catch (OpenR66ProtocolNoSslException e1) {
R66Result finalValue = new R66Result(
new OpenR66ProtocolSystemException("No SSL support", e1),
localChannelReference.getSession(), true, ErrorCode.ConnectionImpossible, null);
logger.error("Authent is Invalid due to no SSL: {}", e1.getMessage());
if (localChannelReference.getRemoteId().compareTo(ChannelUtils.NOCHANNEL) == 0) {
ConnectionErrorPacket error = new ConnectionErrorPacket(
"Cannot connect to localChannel since SSL is not supported", null);
try {
ChannelUtils.writeAbstractLocalPacket(localChannelReference, error, true);
} catch (OpenR66ProtocolPacketException e) {
}
}
localChannelReference.invalidateRequest(finalValue);
localChannelReference.getLocalChannel().close();
throw new OpenR66ProtocolNetworkException(e1);
}
logger.debug("Will send request of connection validation");
localChannelReference.sessionNewState(AUTHENTR);
try {
ChannelUtils.writeAbstractLocalPacket(localChannelReference, authent, true);
} catch (OpenR66ProtocolPacketException e) {
R66Result finalValue = new R66Result(
new OpenR66ProtocolSystemException("Wrong Authent Protocol", e),
localChannelReference.getSession(), true, ErrorCode.ConnectionImpossible, null);
logger.error("Authent is Invalid due to protocol: {}", e.getMessage());
localChannelReference.invalidateRequest(finalValue);
if (localChannelReference.getRemoteId() != ChannelUtils.NOCHANNEL) {
ConnectionErrorPacket error = new ConnectionErrorPacket(
"Cannot connect to localChannel since Authent Protocol is invalid", null);
try {
ChannelUtils.writeAbstractLocalPacket(localChannelReference, error, true);
} catch (OpenR66ProtocolPacketException e1) {
}
}
localChannelReference.getLocalChannel().close();
throw new OpenR66ProtocolNetworkException("Bad packet", e);
}
R66Future future = localChannelReference.getFutureValidateConnection();
if (future.isFailed()) {
logger.debug("Will close NETWORK channel since Future cancelled: {}",
future);
R66Result finalValue = new R66Result(
new OpenR66ProtocolSystemException(
"Out of time or Connection invalid during Authentication"),
localChannelReference.getSession(), true, ErrorCode.ConnectionImpossible, null);
logger.info("Authent is Invalid due to: {} {}", finalValue.getException().getMessage(),
future.toString());
localChannelReference.invalidateRequest(finalValue);
if (localChannelReference.getRemoteId() != ChannelUtils.NOCHANNEL) {
ConnectionErrorPacket error = new ConnectionErrorPacket(
"Cannot connect to localChannel with Out of Time", null);
try {
ChannelUtils.writeAbstractLocalPacket(localChannelReference, error, true);
} catch (OpenR66ProtocolPacketException e) {
}
}
localChannelReference.getLocalChannel().close().awaitUninterruptibly(Configuration.RETRYINMS * 2);
throw new OpenR66ProtocolNetworkException(
"Cannot validate connection: " + future.getResult(), future
.getCause());
}
}
/**
* Add a new NetworkChannel from connection
*
* @param channel
* @throws OpenR66ProtocolRemoteShutdownException
*/
public static NetworkChannelReference addNetworkChannel(Channel channel)
throws OpenR66ProtocolRemoteShutdownException {
SocketAddress socketAddress = channel.remoteAddress();
WaarpLock socketLock = getChannelLock(socketAddress);
//socketLock.lock(Configuration.WAITFORNETOP, TimeUnit.MILLISECONDS);
socketLock.lock();
try {
NetworkChannelReference nc = null;
try {
nc = getRemoteChannel(socketAddress);
} catch (OpenR66ProtocolNoDataException e1) {
}
if (nc == null) {
// not an issue: needs to be created
nc = new NetworkChannelReference(channel, socketLock);
addNCR(nc);
}
return nc;
} finally {
socketLock.unlock();
}
}
/**
* To be called when a remote server seems to be down for a while, so to not retry immediately
*
* @param socketAddress
*/
public static void proposeShutdownNetworkChannel(SocketAddress socketAddress) {
WaarpLock lock = getChannelLock(socketAddress);
lock.lock(Configuration.WAITFORNETOP, TimeUnit.MILLISECONDS);
try {
logger.info("Seem Shutdown: {}", socketAddress);
if (containsShutdownNCR(socketAddress)) {
// already done
logger.debug("Already set as shutdown");
return;
}
if (containsBlacklistNCR(socketAddress)) {
// already done
logger.debug("Already set as blocked");
return;
}
if (containsNCR(socketAddress)) {
// already done
logger.debug("Still existing so shutdown is refused");
return;
}
logger.warn(
"This host address will be set as unavailable for 3xTIMEOUT since not reacheable multiple times: {}",
socketAddress);
NetworkChannelReference networkChannelReference = new NetworkChannelReference(socketAddress, lock);
addShutdownNCR(networkChannelReference);
R66ShutdownNetworkChannelTimerTask timerTask;
try {
timerTask = new R66ShutdownNetworkChannelTimerTask(networkChannelReference, false);
Configuration.configuration.getTimerClose().newTimeout(timerTask,
Configuration.configuration.getTIMEOUTCON() * 3, TimeUnit.MILLISECONDS);
} catch (OpenR66RunnerErrorException e) {
// ignore
}
} finally {
lock.unlock();
}
}
/**
* Shutdown one Network Channel
*
* @param networkChannelReference
*/
private static void shuttingDownNetworkChannelInternal(NetworkChannelReference networkChannelReference) {
logger.info("Shutdown: {}", networkChannelReference);
if (containsShutdownNCR(networkChannelReference)) {
// already done
logger.debug("Already set as shutdown");
return;
}
logger.debug("Set as shutdown");
if (networkChannelReference != null) {
addShutdownNCR(networkChannelReference);
if (!networkChannelReference.isShuttingDown) {
networkChannelReference.shutdownAllLocalChannels();
}
R66ShutdownNetworkChannelTimerTask timerTask;
try {
timerTask = new R66ShutdownNetworkChannelTimerTask(networkChannelReference, false);
Configuration.configuration.getTimerClose().newTimeout(timerTask,
Configuration.configuration.getTIMEOUTCON() * 3, TimeUnit.MILLISECONDS);
} catch (OpenR66RunnerErrorException e) {
// ignore
}
}
}
/**
* Shutdown one Network Channel
*
* @param networkChannelReference
*/
public static void shuttingDownNetworkChannel(NetworkChannelReference networkChannelReference) {
shuttingDownNetworkChannelInternal(networkChannelReference);
}
/**
* Shutdown a NetworkChannel and add it to BlaclList
*
* @param networkChannelReference
* @return True if this channel is now blacklisted for a while
*/
public static boolean shuttingDownNetworkChannelBlackList(NetworkChannelReference networkChannelReference) {
shuttingDownNetworkChannelInternal(networkChannelReference);
if (!Configuration.configuration.isBlacklistBadAuthent()) {
return false;
}
if (containsBlacklistNCR(networkChannelReference)) {
return false;
}
addBlacklistNCR(networkChannelReference);
R66ShutdownNetworkChannelTimerTask timerTask;
try {
timerTask = new R66ShutdownNetworkChannelTimerTask(networkChannelReference, true);
Configuration.configuration.getTimerClose().newTimeout(timerTask,
Configuration.configuration.getTIMEOUTCON() * 10, TimeUnit.MILLISECONDS);
} catch (OpenR66RunnerErrorException e) {
// ignore
}
return true;
}
/**
*
* @param channel
* @return True if this channel is blacklisted
*/
public static boolean isBlacklisted(Channel channel) {
if (!Configuration.configuration.isBlacklistBadAuthent()) {
return false;
}
SocketAddress address = channel.remoteAddress();
if (address == null) {
return false;
}
NetworkChannelReference networkChannelReference = getBlacklistNCR(address);
return (networkChannelReference != null);
}
/**
*
* @param address
* @return True if this address (associated channel) is currently in shutdown (or if this channel is not valid)
*/
public static boolean isShuttingdownNetworkChannel(SocketAddress address) {
return !isAddressValid(address);
}
/**
* Shutdown NetworkChannelReference as client
*
* @param requester
* @return True if shutdown occurs
*/
public static boolean shuttingdownNetworkChannelsPerHostID(String requester) {
if (requester == null) {
return false;
}
ClientNetworkChannels clientNetworkChannels = clientNetworkChannelsPerHostId.get(requester);
logger.debug("AddClient: shutdown previous exist? " + (clientNetworkChannels != null) + " for :" + requester);
if (clientNetworkChannels != null) {
return clientNetworkChannels.shutdownAllNetworkChannels();
}
return false;
}
/**
* Add a requester channel
*
* @param networkChannelReference
* @param requester
*/
public static void addClient(NetworkChannelReference networkChannelReference, String requester) {
if (networkChannelReference != null && requester != null) {
ClientNetworkChannels clientNetworkChannels = clientNetworkChannelsPerHostId.get(requester);
if (clientNetworkChannels == null) {
clientNetworkChannels = new ClientNetworkChannels(requester);
clientNetworkChannelsPerHostId.put(requester, clientNetworkChannels);
}
clientNetworkChannels.add(networkChannelReference);
logger.debug("AddClient: add count? " + clientNetworkChannels.size() + " for " + requester);
}
}
private static void removeClient(NetworkChannelReference networkChannelReference, String requester,
ClientNetworkChannels clientNetworkChannels) {
if (networkChannelReference != null && clientNetworkChannels != null && requester != null) {
clientNetworkChannels.remove(networkChannelReference);
logger.debug("removeClient: remove for :" + requester + " still " + clientNetworkChannels.size());
if (clientNetworkChannels.isEmpty()) {
clientNetworkChannelsPerHostId.remove(requester);
}
}
}
/**
*
* @param requester
* @return The number of NetworkChannels associated with this requester
*/
public static int getNumberClients(String requester) {
ClientNetworkChannels clientNetworkChannels = clientNetworkChannelsPerHostId.get(requester);
if (clientNetworkChannels != null) {
return clientNetworkChannels.size();
}
return 0;
}
/**
* Force remove of NetworkChannelReference when it is closed
*
* @param networkChannelReference
*/
public static void closedNetworkChannel(NetworkChannelReference networkChannelReference) {
if (networkChannelReference == null) {
return;
}
try {
if (!networkChannelReference.isShuttingDown) {
networkChannelReference.shutdownAllLocalChannels();
}
logger.debug("NC left: {}", networkChannelReference);
removeNCR(networkChannelReference);
if (networkChannelReference.clientNetworkChannels != null) {
String requester = networkChannelReference.clientNetworkChannels.getHostId();
removeClient(networkChannelReference, requester, networkChannelReference.clientNetworkChannels);
} else if (networkChannelReference.getHostId() != null) {
String requester = networkChannelReference.getHostId();
ClientNetworkChannels clientNetworkChannels = clientNetworkChannelsPerHostId.get(requester);
if (clientNetworkChannels != null) {
removeClient(networkChannelReference, requester, clientNetworkChannels);
}
}
} finally {
removeChannelLock();
}
}
/**
* Force remove of NetworkChannelReference when it is closed
*
* @param address
*/
public static void closedNetworkChannel(SocketAddress address) {
if (address == null) {
return;
}
NetworkChannelReference networkChannelReference =
networkChannelOnSocketAddressConcurrentHashMap.get(address.hashCode());
closedNetworkChannel(networkChannelReference);
}
/**
* Class to close the Network Channel if after some delays it has really no Local Channel
* attached
*
* @author Frederic Bregier
*
*/
private static class CloseFutureChannel implements TimerTask {
private static final ConcurrentSet<ChannelId> inCloseRunning = new ConcurrentSet<ChannelId>();
private final NetworkChannelReference networkChannelReference;
/**
* @param networkChannelReference
* @param requester
* @throws OpenR66RunnerErrorException
*/
private CloseFutureChannel(NetworkChannelReference networkChannelReference)
throws OpenR66RunnerErrorException {
if (!inCloseRunning.add(networkChannelReference.channel.id()))
throw new OpenR66RunnerErrorException("Already scheduled");
this.networkChannelReference = networkChannelReference;
}
public void run(Timeout timeout) throws Exception {
networkChannelReference.lock.lock(Configuration.WAITFORNETOP, TimeUnit.MILLISECONDS);
try {
logger.debug("NC count: {}", networkChannelReference);
if (networkChannelReference.nbLocalChannels() <= 0) {
long time = networkChannelReference.checkLastTime(Configuration.configuration.getTIMEOUTCON() * 2);
if (time > Configuration.RETRYINMS) {
logger.debug("NC reschedule at " + time + " : {}", networkChannelReference);
// will re execute this request later on
time = (time / 10) * 10 + 100; // round to 10
Configuration.configuration.getTimerClose().newTimeout(this, time,
TimeUnit.MILLISECONDS);
return;
}
logger.info("Closing NETWORK channel {}", networkChannelReference);
networkChannelReference.isShuttingDown = true;
WaarpSslUtility.closingSslChannel(networkChannelReference.channel);
}
inCloseRunning.remove(networkChannelReference.channel.id());
} finally {
networkChannelReference.lock.unlock();
}
}
}
/**
* Check if closing of the localChannel will bring future closing of NetworkChannel
*
* @param networkChannelReference
* @param localChannelReference
* @return the number of local channel still connected to this channel
*/
public static int checkClosingNetworkChannel(NetworkChannelReference networkChannelReference,
LocalChannelReference localChannelReference) {
networkChannelReference.lock.lock(Configuration.WAITFORNETOP, TimeUnit.MILLISECONDS);
try {
logger.debug("Close con: " + networkChannelReference);
if (localChannelReference != null) {
networkChannelReference.remove(localChannelReference);
}
int count = networkChannelReference.nbLocalChannels();
if (count <= 0) {
CloseFutureChannel cfc;
try {
cfc = new CloseFutureChannel(networkChannelReference);
Configuration.configuration.getTimerClose().
newTimeout(cfc, Configuration.configuration.getTIMEOUTCON() * 2,
TimeUnit.MILLISECONDS);
} catch (OpenR66RunnerErrorException e) {
} catch (IllegalStateException e) {
}
}
logger.debug("NC left: {}", networkChannelReference);
return count;
} finally {
networkChannelReference.lock.unlock();
}
}
/**
*
* @param address
* @param host
* @return a number > 0 if a connection is still active on this socket or for this host
*/
public static int nbAttachedConnection(SocketAddress address, String host) {
logger.debug("nbAttachedConnection: "
+ networkChannelOnSocketAddressConcurrentHashMap.containsKey(address.hashCode()) + ":"
+ getNumberClients(host));
return (networkChannelOnSocketAddressConcurrentHashMap.containsKey(address.hashCode()) ? 1
: 0)
+ getNumberClients(host);
}
/**
*
* @param address
* @return True if this socket Address is currently valid for connection
*/
private static boolean isAddressValid(SocketAddress address) {
if (R66ShutdownHook.isShutdownStarting()) {
logger.debug("IS IN SHUTDOWN");
return false;
}
if (address == null) {
logger.debug("ADDRESS IS NULL");
return false;
}
try {
NetworkChannelReference networkChannelReference = getRemoteChannel(address);
logger.debug("IS IN SHUTDOWN: " + networkChannelReference.isShuttingDown);
return !networkChannelReference.isShuttingDown;
} catch (OpenR66ProtocolRemoteShutdownException e) {
logger.debug("ALREADY IN SHUTDOWN");
return false;
} catch (OpenR66ProtocolNoDataException e) {
logger.debug("NOT FOUND SO NO SHUTDOWN");
return true;
}
}
/**
* Returns the NetworkChannelReference if it exists associated with this address
*
* @param address
* @return NetworkChannelReference
* @throws OpenR66ProtocolRemoteShutdownException
* @throws OpenR66ProtocolNoDataException
*/
private static NetworkChannelReference getRemoteChannel(SocketAddress address)
throws OpenR66ProtocolRemoteShutdownException,
OpenR66ProtocolNoDataException {
if (R66ShutdownHook.isShutdownStarting()) {
logger.debug("IS IN SHUTDOWN");
throw new OpenR66ProtocolRemoteShutdownException(
"Local Host already in shutdown");
}
if (address == null) {
logger.debug("ADDRESS IS NULL");
throw new OpenR66ProtocolRemoteShutdownException(
"Cannot connect to remote server since address is not specified");
}
NetworkChannelReference nc = getShutdownNCR(address);
if (nc != null) {
logger.debug("HOST STILL IN SHUTDOWN STATUS: {}", address);
throw new OpenR66ProtocolRemoteShutdownException(
"Remote Host already in shutdown");
}
nc = getBlacklistNCR(address);
if (nc != null) {
logger.debug("HOST IN BLACKLISTED STATUS: {}", address);
throw new OpenR66ProtocolRemoteShutdownException(
"Remote Host is blacklisted");
}
nc = getNCR(address);
if (nc != null && (nc.isShuttingDown || !nc.channel().isActive())) {
logger.debug("HOST IS DisActive: {}", address);
throw new OpenR66ProtocolRemoteShutdownException(
"Remote Host is disActive");
}
if (nc == null) {
throw new OpenR66ProtocolNoDataException("Channel not found");
}
return nc;
}
/**
*
* @param channel
* @return the associated NetworkChannelReference immediately (if known)
*/
public static final NetworkChannelReference getImmediateNetworkChannel(Channel channel) {
if (channel.remoteAddress() != null) {
return getNCR(channel.remoteAddress());
}
return null;
}
/**
* Remover of Shutdown Remote Host
*
* @author Frederic Bregier
*
*/
private static class R66ShutdownNetworkChannelTimerTask implements TimerTask {
private static final ConcurrentSet<ChannelId> inShutdownRunning = new ConcurrentSet<ChannelId>();
private static final ConcurrentSet<ChannelId> inBlacklistedRunning = new ConcurrentSet<ChannelId>();
/**
* NCR to remove
*/
private final NetworkChannelReference ncr;
private final boolean isBlacklisted;
/**
* Constructor from type
*
* @param href
* @throws OpenR66RunnerErrorException
*/
public R66ShutdownNetworkChannelTimerTask(NetworkChannelReference ncr, boolean blackListed)
throws OpenR66RunnerErrorException {
super();
if (blackListed) {
if (!inBlacklistedRunning.add(ncr.channel.id())) {
throw new OpenR66RunnerErrorException("Already scheduled");
}
} else {
if (ncr.channel != null && !inShutdownRunning.add(ncr.channel.id())) {
throw new OpenR66RunnerErrorException("Already scheduled");
}
}
this.ncr = ncr;
this.isBlacklisted = blackListed;
}
public void run(Timeout timeout) throws Exception {
if (isBlacklisted) {
logger.debug("Will remove Blacklisted for : {}", ncr);
removeBlacklistNCR(ncr);
inBlacklistedRunning.remove(ncr.channel.id());
return;
}
logger.debug("Will remove Shutdown for : {}", ncr);
if (ncr.channel != null && ncr.channel.isActive()) {
WaarpSslUtility.closingSslChannel(ncr.channel);
}
removeShutdownNCR(ncr);
if (ncr.channel != null) {
inShutdownRunning.remove(ncr.channel.id());
}
}
}
public static ExecutorService getRetrieveExecutor() {
return retrieveExecutor;
}
public static ConcurrentHashMap<Integer, RetrieveRunner> getRetrieveRunnerConcurrentHashMap() {
return retrieveRunnerConcurrentHashMap;
}
/**
* Start retrieve operation
*
* @param session
* @param channel
*/
public static void runRetrieve(R66Session session, LocalChannel channel) {
RetrieveRunner retrieveRunner = new RetrieveRunner(session, channel);
retrieveRunnerConcurrentHashMap.put(session.getLocalChannelReference().getLocalId(),
retrieveRunner);
retrieveRunner.setDaemon(true);
Configuration.configuration.getLocalWorkerGroup().execute(retrieveRunner);
}
/**
* Stop a retrieve operation
*
* @param localChannelReference
*/
public static void stopRetrieve(LocalChannelReference localChannelReference) {
RetrieveRunner retrieveRunner =
retrieveRunnerConcurrentHashMap.remove(localChannelReference.getLocalId());
if (retrieveRunner != null) {
retrieveRunner.stopRunner();
}
}
/**
* Normal end of a Retrieve Operation
*
* @param localChannelReference
*/
public static void normalEndRetrieve(LocalChannelReference localChannelReference) {
retrieveRunnerConcurrentHashMap.remove(localChannelReference.getLocalId());
}
/**
* Stop all Retrieve Executors
*/
public static void closeRetrieveExecutors() {
retrieveExecutor.shutdownNow();
}
/**
* Close all Network Ttransaction
*/
public void closeAll() {
closeAll(true);
}
/**
* Close all Network Ttransaction
*/
public void closeAll(boolean quickShutdown) {
logger.debug("close All Network Channels");
if (!Configuration.configuration.isServer()) {
R66ShutdownHook.shutdownHook.launchFinalExit();
}
closeRetrieveExecutors();
networkChannelGroup.close();
try {
Thread.sleep(Configuration.WAITFORNETOP);
} catch (InterruptedException e) {
}
DbAdmin.closeAllConnection();
Configuration.configuration.clientStop(quickShutdown);
if (!Configuration.configuration.isServer()) {
logger.debug("Last action before exit");
ChannelUtils.stopLogger();
}
}
}