NetworkChannelReference.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.protocol.networkhandler;
import io.netty.channel.Channel;
import org.waarp.common.future.WaarpLock;
import org.waarp.common.logging.SysErrLogger;
import org.waarp.common.logging.WaarpLogger;
import org.waarp.common.logging.WaarpLoggerFactory;
import org.waarp.common.lru.ConcurrentUtility;
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.DbTaskRunner;
import org.waarp.openr66.database.data.DbTaskRunner.TASKSTEP;
import org.waarp.openr66.protocol.configuration.Configuration;
import org.waarp.openr66.protocol.exception.OpenR66ProtocolRemoteShutdownException;
import org.waarp.openr66.protocol.exception.OpenR66ProtocolSystemException;
import org.waarp.openr66.protocol.localhandler.LocalChannelReference;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import static org.waarp.openr66.protocol.configuration.Configuration.*;
/**
* NetworkChannelReference object to keep Network channel open while some local
* channels are attached to it.
*/
public class NetworkChannelReference {
/**
* Internal Logger
*/
private static final WaarpLogger logger =
WaarpLoggerFactory.getLogger(NetworkChannelReference.class);
private static final LocalChannelReference[] LCR_0_LENGTH =
new LocalChannelReference[0];
/**
* Does this Network Channel is in shutdown
*/
protected boolean isShuttingDown;
/**
* Associated LocalChannelReference
*/
private final Set<LocalChannelReference> localChannelReferences =
ConcurrentUtility.newConcurrentSet();
/**
* Network Channel
*/
protected final Channel channel;
/**
* Remote network address (when valid)
*/
protected final SocketAddress networkAddress;
/**
* Remote IP address
*/
private final String hostAddress;
/**
* Remote Host Id
*/
private String hostId;
/**
* ClientNetworkChannels object that contains this NetworkChannelReference
*/
protected ClientNetworkChannels clientNetworkChannels;
/**
* Associated lock
*/
protected final WaarpLock lock;
/**
* Last Time in ms this channel was used by a LocalChannel
*/
private long lastTimeUsed = System.currentTimeMillis();
/**
* Is this channel multiplexed using Ssl
*/
private final boolean isSSL;
public NetworkChannelReference(final Channel networkChannel,
final WaarpLock lock, final boolean isSSL) {
channel = networkChannel;
networkAddress = channel.remoteAddress();
hostAddress =
((InetSocketAddress) networkAddress).getAddress().getHostAddress();
this.lock = lock;
this.isSSL = isSSL;
}
public NetworkChannelReference(final SocketAddress address,
final WaarpLock lock, final boolean isSSL) {
channel = null;
networkAddress = address;
hostAddress =
((InetSocketAddress) networkAddress).getAddress().getHostAddress();
this.lock = lock;
this.isSSL = isSSL;
}
public final boolean isSSL() {
return isSSL;
}
public final void add(final LocalChannelReference localChannel)
throws OpenR66ProtocolRemoteShutdownException {
// lock is of no use since caller is itself in locked situation for the very same lock
if (isShuttingDown) {
throw new OpenR66ProtocolRemoteShutdownException(
"Current NetworkChannelReference is closed");
}
use();
localChannelReferences.add(localChannel);
}
/**
* To set the last time used
*/
public final void use() {
if (!isShuttingDown) {
lastTimeUsed = System.currentTimeMillis();
}
}
/**
* To set the last time used when correct
*
* @return True if last time used is set
*/
public final boolean useIfUsed() {
if (!isShuttingDown && !localChannelReferences.isEmpty()) {
lastTimeUsed = System.currentTimeMillis();
return true;
}
return false;
}
/**
* Remove one LocalChanelReference, closing it if necessary.
*
* @param localChannel
*/
public final void closeAndRemove(final LocalChannelReference localChannel) {
if (!localChannel.getFutureRequest().isDone()) {
localChannel.close();
}
remove(localChannel);
}
/**
* Remove one LocalChanelReference
*
* @param localChannel
*/
public final void remove(final LocalChannelReference localChannel) {
localChannelReferences.remove(localChannel);
// Do not since it prevents shutdown: lastTimeUsed = System.currentTimeMillis()
}
/**
* Shutdown All Local Channels associated with this NCR
*/
public final void shutdownAllLocalChannels() {
lock.lock(Configuration.WAITFORNETOP, TimeUnit.MILLISECONDS);
try {
logger.info("Will shutdown all local channels");
isShuttingDown = true;
final LocalChannelReference[] localChannelReferenceArray =
localChannelReferences.toArray(LCR_0_LENGTH);
final ArrayList<LocalChannelReference> toCloseLater =
new ArrayList<LocalChannelReference>();
for (final LocalChannelReference localChannelReference : localChannelReferenceArray) {
localChannelReference.getFutureRequest().awaitOrInterruptible(
Configuration.configuration.getTimeoutCon() / 3);
if (!localChannelReference.getFutureRequest().isDone()) {
localChannelReference.getFutureValidRequest().awaitOrInterruptible(
Configuration.configuration.getTimeoutCon() / 3);
if (localChannelReference.getFutureValidRequest().isDone() &&
localChannelReference.getFutureValidRequest().isFailed()) {
toCloseLater.add(localChannelReference);
continue;
} else {
final R66Result finalValue =
new R66Result(localChannelReference.getSession(), true,
ErrorCode.Shutdown, null);
if (localChannelReference.getSession() != null) {
try {
localChannelReference.getSession()
.tryFinalizeRequest(finalValue);
} catch (final OpenR66RunnerErrorException ignored) {
// nothing
} catch (final OpenR66ProtocolSystemException ignored) {
// nothing
}
}
}
}
localChannelReference.close();
}
try {
Thread.sleep(Configuration.WAITFORNETOP);
} catch (final InterruptedException e) {//NOSONAR
SysErrLogger.FAKE_LOGGER.ignoreLog(e);
}
for (final LocalChannelReference localChannelReference : toCloseLater) {
localChannelReference.getFutureRequest().awaitOrInterruptible(
Configuration.configuration.getTimeoutCon() / 3);
localChannelReference.close();
}
toCloseLater.clear();
} finally {
lock.unlock();
}
}
/**
* @param localChannelReference the localChannelReference to be closed
*
* @return True if the localChannelReference is the only one still active or there is no more LCR
*/
public final boolean isLastLocalChannelActive(
final LocalChannelReference localChannelReference) {
final boolean someActive = isSomeLocalChannelsActive();
return
(someActive && localChannelReferences.contains(localChannelReference) &&
localChannelReferences.size() == 1) ||
localChannelReferences.isEmpty();
}
/**
* @return -1 if not allowed, 0 if allowed, else time in ms before ready to recheck
*/
public final long shutdownAllowed() {
lock.lock(Configuration.WAITFORNETOP, TimeUnit.MILLISECONDS);
try {
logger.debug("NC count: {}", this);
if (nbLocalChannels() <= 0) {
boolean reallyShutdownNetwork = true;
for (int i = 0; i < RETRYNB; i++) {
try {
Thread.sleep(RETRYINMS);
} catch (final InterruptedException e) { // NOSONAR
SysErrLogger.FAKE_LOGGER.ignoreLog(e);
}
if (nbLocalChannels() != 0) {
reallyShutdownNetwork = false;
break;
}
}
if (reallyShutdownNetwork) {
long time =
checkLastTime(Configuration.configuration.getTimeoutCon() * 2);
if (time > Configuration.RETRYINMS &&
Configuration.configuration.isTimerCloseReady()) {
logger.debug("NC reschedule at {} : {}", time, this);
// will re execute this request later on
time = (time / 10) * 10 + 100; // round to 10
return time;
}
logger.info("Closing NETWORK channel {}", this);
return 0;
} else {
use();
logger.debug("Ignore closing Network channel");
return -1;
}
}
return -1;
} finally {
lock.unlock();
}
}
public final void lockNetwork() {
lock.lock(Configuration.WAITFORNETOP, TimeUnit.MILLISECONDS);
}
public final void unlockNetwork() {
lock.unlock();
}
public final int nbLocalChannels() {
return localChannelReferences.size();
}
/**
* @return True if at least one LocalChannel is not yet finished (OK or Error)
*/
public final boolean isSomeLocalChannelsActive() {
lock.lock(Configuration.WAITFORNETOP, TimeUnit.MILLISECONDS);
try {
for (final LocalChannelReference localChannelReference : localChannelReferences) {
final R66Session session = localChannelReference.getSession();
if (session != null) {
final DbTaskRunner runner = session.getRunner();
if (runner != null && !runner.isFinished() &&
runner.getGlobalStep() != TASKSTEP.NOTASK) {
return true;
}
}
}
return false;
} finally {
lock.unlock();
}
}
@Override
public final String toString() {
return "NC: " + hostId + ':' + (channel != null && channel.isActive()) +
' ' + networkAddress + " Count: " + localChannelReferences.size();
}
@Override
public final boolean equals(final Object obj) {
if (obj instanceof NetworkChannelReference) {
final NetworkChannelReference obj2 = (NetworkChannelReference) obj;
if (obj2.channel == null || channel == null) {
return false;
}
return obj2.channel.id().compareTo(channel.id()) == 0;
}
return false;
}
@Override
public final int hashCode() {
if (channel == null) {
return Integer.MIN_VALUE;
}
return channel.id().hashCode();
}
/**
* @return the hashcode for the global remote networkaddress
*/
public final int getSocketHashCode() {
return networkAddress.hashCode();
}
/**
* Used for BlackList
*
* @return the hashcode for the address
*/
public final int getAddressHashCode() {
return hostAddress.hashCode();
}
/**
* Check if the last time used is ok with a delay applied to the current
* time
* (timeout)
*
* @param delay
*
* @return <= 0 if OK, else > 0 (should send a KeepAlive or wait that time
* in ms)
*/
public final long checkLastTime(final long delay) {
return lastTimeUsed + delay - System.currentTimeMillis();
}
/**
* @return the isShuttingDown
*/
public final boolean isShuttingDown() {
return isShuttingDown;
}
/**
* @return the channel
*/
public final Channel channel() {
return channel;
}
/**
* @return the hostId
*/
public final String getHostId() {
return hostId;
}
/**
* @param hostId the hostId to set
*/
public final void setHostId(final String hostId) {
this.hostId = hostId;
}
/**
* @return the lastTimeUsed
*/
public final long getLastTimeUsed() {
return lastTimeUsed;
}
}