ConnectionActions.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.localhandler;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import org.waarp.common.command.exception.Reply421Exception;
import org.waarp.common.command.exception.Reply530Exception;
import org.waarp.common.database.exception.WaarpDatabaseException;
import org.waarp.common.digest.FilesystemBasedDigest;
import org.waarp.common.logging.SysErrLogger;
import org.waarp.common.logging.WaarpLogger;
import org.waarp.common.logging.WaarpLoggerFactory;
import org.waarp.openr66.commander.ClientRunner;
import org.waarp.openr66.context.ErrorCode;
import org.waarp.openr66.context.R66FiniteDualStates;
import org.waarp.openr66.context.R66Result;
import org.waarp.openr66.context.R66Session;
import org.waarp.openr66.context.authentication.R66Auth;
import org.waarp.openr66.context.task.exception.OpenR66RunnerErrorException;
import org.waarp.openr66.database.data.DbHostAuth;
import org.waarp.openr66.database.data.DbTaskRunner;
import org.waarp.openr66.protocol.configuration.Configuration;
import org.waarp.openr66.protocol.configuration.Messages;
import org.waarp.openr66.protocol.exception.OpenR66Exception;
import org.waarp.openr66.protocol.exception.OpenR66ProtocolBusinessCancelException;
import org.waarp.openr66.protocol.exception.OpenR66ProtocolBusinessException;
import org.waarp.openr66.protocol.exception.OpenR66ProtocolBusinessNoWriteBackException;
import org.waarp.openr66.protocol.exception.OpenR66ProtocolBusinessQueryAlreadyFinishedException;
import org.waarp.openr66.protocol.exception.OpenR66ProtocolBusinessQueryStillRunningException;
import org.waarp.openr66.protocol.exception.OpenR66ProtocolBusinessRemoteFileNotFoundException;
import org.waarp.openr66.protocol.exception.OpenR66ProtocolBusinessStopException;
import org.waarp.openr66.protocol.exception.OpenR66ProtocolNotAuthenticatedException;
import org.waarp.openr66.protocol.exception.OpenR66ProtocolPacketException;
import org.waarp.openr66.protocol.exception.OpenR66ProtocolSystemException;
import org.waarp.openr66.protocol.localhandler.packet.AuthentPacket;
import org.waarp.openr66.protocol.localhandler.packet.ConnectionErrorPacket;
import org.waarp.openr66.protocol.localhandler.packet.ErrorPacket;
import org.waarp.openr66.protocol.localhandler.packet.StartupPacket;
import org.waarp.openr66.protocol.networkhandler.NetworkServerHandler;
import org.waarp.openr66.protocol.networkhandler.NetworkTransaction;
import org.waarp.openr66.protocol.utils.ChannelCloseTimer;
import org.waarp.openr66.protocol.utils.ChannelUtils;
import org.waarp.openr66.protocol.utils.R66Future;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import static org.waarp.openr66.context.R66FiniteDualStates.*;
/**
* Class to implement actions related to general connection handler:
* channelClosed, startup, authentication,
* and error. Used to store and retrieve the session information.
*/
public abstract class ConnectionActions {
/**
* Internal Logger
*/
private static final WaarpLogger logger =
WaarpLoggerFactory.getLogger(ConnectionActions.class);
/**
* Session
*/
protected R66Session session;
/**
* Local Channel Reference
*/
protected LocalChannelReference localChannelReference;
/**
* Global Digest in receive
*/
protected FilesystemBasedDigest globalDigest;
/**
* Global Digest in receive using local hash if necessary
*/
protected FilesystemBasedDigest localDigest;
final void businessError() {
if (session.getBusinessObject() != null) {
session.getBusinessObject().checkAtError(session);
}
}
/**
* @return the session
*/
public final R66Session getSession() {
return session;
}
/**
* @return the localChannelReference
*/
public final LocalChannelReference getLocalChannelReference() {
return localChannelReference;
}
/**
* Operations to ensure that channel closing is done correctly
*/
public final void channelClosed() {
if (session.getState() == CLOSEDCHANNEL) {
return;
}
final DbTaskRunner runner = session.getRunner();
try {
if (logger.isDebugEnabled()) {
logger.debug("Local Server Channel Closed: {} {}",
localChannelReference != null? localChannelReference :
"no LocalChannelReference",
runner != null? runner.toShortString() : "no runner");
}
// clean session objects like files
boolean mustFinalize = true;
final R66Future transfer = localChannelReference != null?
localChannelReference.getFutureRequest() : null;
if (transfer != null && transfer.isDone()) {
// already done
mustFinalize = false;
} else {
if (localChannelReference != null) {
final R66Future fvr = localChannelReference.getFutureValidRequest();
fvr.awaitOrInterruptible(Configuration.configuration.getTimeoutCon());
if (fvr.isDone()) {
if (!fvr.isSuccess()) {
// test if remote server was Overloaded
if (fvr.getResult() != null &&
fvr.getResult().getCode() == ErrorCode.ServerOverloaded) {
// ignore
mustFinalize = false;
}
} else {
mustFinalize = false;
}
}
logger.debug("Must Finalize: {}", mustFinalize);
if (mustFinalize) {
session.newState(ERROR);
final R66Result finalValue = new R66Result(
new OpenR66ProtocolSystemException(
runner != null? Messages.getString("LocalServerHandler.4") :
Messages.getString("HttpSslHandler.37")),
//$NON-NLS-1$
session, true, ErrorCode.FinalOp, runner); // True since closed
try {
tryFinalizeRequest(finalValue);
} catch (final OpenR66Exception ignored) {
// ignore
}
}
}
}
if (mustFinalize && runner != null && runner.isRequestOnRequested() &&
localChannelReference != null) {
// Since requested : log
final R66Result result = transfer.getResult();
if (transfer.isDone() && transfer.isSuccess()) {
logger.info("TRANSFER REQUESTED RESULT: SUCCESS {}",
(result != null? result : "no result"));
} else {
logger.error("TRANSFER REQUESTED RESULT: FAILURE " +
(result != null? result.toString() : "no result"));
}
}
session.setStatus(50);
session.newState(CLOSEDCHANNEL);
session.partialClear();
session.setStatus(51);
if (localChannelReference != null) {
if (localChannelReference.getDbSession() != null) {
localChannelReference.getDbSession().endUseConnection();
logger.debug("End Use Connection");
}
NetworkTransaction.checkClosingNetworkChannel(
localChannelReference.getNetworkChannelObject(),
localChannelReference);
session.setStatus(52);
} else {
logger.debug(
"Local Server Channel Closed but no LocalChannelReference");
}
// Now if runner is not yet finished, finish it by force
if (mustFinalize && localChannelReference != null && !transfer.isDone()) {
final R66Result finalValue = new R66Result(
new OpenR66ProtocolSystemException(
Messages.getString("LocalServerHandler.11")),
//$NON-NLS-1$
session, true, ErrorCode.FinalOp, runner);
localChannelReference.invalidateRequest(finalValue);
// In case stop the attached thread if any
final ClientRunner clientRunner =
localChannelReference.getClientRunner();
if (clientRunner != null) {
try {
Thread.sleep(Configuration.RETRYINMS);
} catch (final InterruptedException e1) {//NOSONAR
SysErrLogger.FAKE_LOGGER.ignoreLog(e1);
}
clientRunner.interrupt();
}
}
} finally {
if (runner != null) {
runner.clean();
}
final LocalTransaction lt =
Configuration.configuration.getLocalTransaction();
if (lt != null && localChannelReference != null) {
localChannelReference.close();
}
}
}
/**
* Create a new Session at startup of the channel
*/
public final void newSession() {
session = new R66Session();
session.setStatus(60);
session.setBusinessObject(
Configuration.configuration.getR66BusinessFactory()
.getBusinessInterface(session));
}
protected final void setLocalChannelReference(
final LocalChannelReference localChannelReference) {
if (localChannelReference != null) {
this.localChannelReference = localChannelReference;
if (session != null) {
session.setLocalChannelReference(localChannelReference);
}
}
}
/**
* Startup of the session and the local channel reference
*
* @param packet
*
* @throws OpenR66ProtocolPacketException
*/
public final void startup(final StartupPacket packet)
throws OpenR66ProtocolPacketException {
packet.clear();
if (localChannelReference == null) {
session.newState(ERROR);
logger.error(Messages.getString("LocalServerHandler.1")); //$NON-NLS-1$
session.setStatus(40);
throw new OpenR66ProtocolPacketException("Cannot startup connection");
}
if (session.getBusinessObject() != null) {
try {
session.getBusinessObject().checkAtConnection(session);
} catch (final OpenR66RunnerErrorException e) {
final ErrorPacket error =
new ErrorPacket("Connection refused by business logic",
ErrorCode.ConnectionImpossible.getCode(),
ErrorPacket.FORWARDCLOSECODE);
ChannelUtils.writeAbstractLocalPacket(localChannelReference, error,
false);
session.setStatus(40);
return;
}
}
session.newState(STARTUP);
localChannelReference.validateStartup(true);
session.setStatus(41);
logger.debug("Startup done for {}", localChannelReference.getLocalId());
}
/**
* Refuse a connection
*
* @param packet
* @param e1
*
* @throws OpenR66ProtocolPacketException
*/
private void refusedConnection(final AuthentPacket packet, Exception e1)
throws OpenR66ProtocolPacketException {
logger.error(Messages.getString("LocalServerHandler.6") + //$NON-NLS-1$
localChannelReference.getNetworkChannel().remoteAddress() +
" : " + packet.getHostId());
if (logger.isDebugEnabled()) {
logger.debug(Messages.getString("LocalServerHandler.6") + //$NON-NLS-1$
localChannelReference.getNetworkChannel().remoteAddress() +
" : " + packet.getHostId(), e1);
}
if (Configuration.configuration.getR66Mib() != null) {
Configuration.configuration.getR66Mib().notifyError(
"Connection not allowed from " +
localChannelReference.getNetworkChannel().remoteAddress() +
" since " + e1.getMessage(), packet.getHostId());
}
DbHostAuth auth = null;
try {
auth = new DbHostAuth(packet.getHostId());
} catch (final WaarpDatabaseException e) {
logger.warn(
"Cannot find the authentication " + packet.getHostId() + " : {}",
e.getMessage());
}
if (auth != null && !auth.isActive()) {
e1 = new Reply530Exception(
"Host is Inactive therefore connection is refused");
}
final R66Result result = new R66Result(new OpenR66ProtocolSystemException(
Messages.getString("LocalServerHandler.6") +
//$NON-NLS-1$
localChannelReference.getNetworkChannel().remoteAddress(), e1), session,
true, ErrorCode.BadAuthent, null);
localChannelReference.invalidateRequest(result);
session.newState(ERROR);
final ErrorPacket error =
new ErrorPacket(e1.getMessage(), ErrorCode.BadAuthent.getCode(),
ErrorPacket.FORWARDCLOSECODE);
ChannelUtils.writeAbstractLocalPacket(localChannelReference, error, false);
localChannelReference.validateConnection(false, result);
final Channel networkchannel = localChannelReference.getNetworkChannel();
final boolean valid =
NetworkTransaction.shuttingDownNetworkChannelBlackList(
localChannelReference.getNetworkChannelObject());
logger.warn(
"Closing and blacklisting NetworkChannelReference since LocalChannel is not authenticated: " +
valid);
ChannelCloseTimer.closeFutureTransaction(this);
ChannelCloseTimer.closeFutureChannel(networkchannel);
businessError();
}
/**
* Authentication
*
* @param packet
*
* @throws OpenR66ProtocolPacketException
*/
public final void authent(final AuthentPacket packet, final boolean isSsl)
throws OpenR66ProtocolPacketException {
logger.debug("AUTHENT {}", packet);
if (packet.isToValidate()) {
if (session.getState() != AUTHENTD) {
session.newState(AUTHENTR);
}
}
logger.debug("LocalChannelReference null? {}",
localChannelReference == null);
if (localChannelReference.getDbSession() != null) {
localChannelReference.getDbSession().useConnection();
}
if (localChannelReference.getNetworkChannelObject() != null) {
localChannelReference.getNetworkChannelObject()
.setHostId(packet.getHostId());
} else {
session.newState(ERROR);
logger.error("Service unavailable: " + packet.getHostId());
final R66Result result = new R66Result(
new OpenR66ProtocolSystemException("Service unavailable"), session,
true, ErrorCode.ConnectionImpossible, null);
localChannelReference.invalidateRequest(result);
final ErrorPacket error = new ErrorPacket("Service unavailable",
ErrorCode.ConnectionImpossible.getCode(),
ErrorPacket.FORWARDCLOSECODE);
ChannelUtils.writeAbstractLocalPacket(localChannelReference, error,
false);
localChannelReference.validateConnection(false, result);
ChannelCloseTimer.closeFutureTransaction(this);
session.setStatus(43);
businessError();
return;
}
try {
session.getAuth().connection(packet.getHostId(), packet.getKey(), isSsl);
} catch (final Reply530Exception e1) {
refusedConnection(packet, e1);
session.setStatus(42);
return;
} catch (final Reply421Exception e1) {
session.newState(ERROR);
logger.error("Service unavailable: " + packet.getHostId() + ": {}",
e1.getMessage());
final R66Result result = new R66Result(
new OpenR66ProtocolSystemException("Service unavailable", e1),
session, true, ErrorCode.ConnectionImpossible, null);
localChannelReference.invalidateRequest(result);
final ErrorPacket error = new ErrorPacket("Service unavailable",
ErrorCode.ConnectionImpossible.getCode(),
ErrorPacket.FORWARDCLOSECODE);
ChannelUtils.writeAbstractLocalPacket(localChannelReference, error,
false);
localChannelReference.validateConnection(false, result);
ChannelCloseTimer.closeFutureTransaction(this);
session.setStatus(43);
businessError();
return;
}
localChannelReference.setPartner(packet.getHostId());
// Now if configuration say to do so: check remote ip address
if (Configuration.configuration.isCheckRemoteAddress() &&
!localChannelReference.getPartner().isProxified()) {
final DbHostAuth host = R66Auth.getServerAuth(packet.getHostId());
boolean toTest = false;
assert host != null;
if (!host.isProxified()) {
if (host.isClient()) {
if (Configuration.configuration.isCheckClientAddress()) {
// 0.0.0.0 so nothing
toTest = !host.isNoAddress();
}
} else {
toTest = true;
}
}
if (toTest) {
// Real address so compare
final String address = host.getAddress();
InetAddress[] inetAddress;
try {
inetAddress = InetAddress.getAllByName(address);
} catch (final UnknownHostException e) {
inetAddress = null;
}
if (inetAddress != null) {
final InetSocketAddress socketAddress =
(InetSocketAddress) session.getRemoteAddress();
boolean found = false;
for (final InetAddress inetAddres : inetAddress) {
if (socketAddress.getAddress().equals(inetAddres)) {
found = true;
break;
}
}
if (!found) {
// error
refusedConnection(packet,
new OpenR66ProtocolNotAuthenticatedException(
"Server IP not authenticated: " +
inetAddress[0] + " compare to " +
socketAddress.getAddress()));
session.setStatus(104);
return;
}
}
}
}
if (session.getBusinessObject() != null) {
try {
session.getBusinessObject().checkAtAuthentication(session);
} catch (final OpenR66RunnerErrorException e) {
refusedConnection(packet, new OpenR66ProtocolNotAuthenticatedException(
e.getMessage()));
session.setStatus(104);
return;
}
}
// Check compression
session.setCompressionEnabled(
localChannelReference.getPartner().isCompression() &&
Configuration.configuration.isCompressionAvailable());
final R66Result result =
new R66Result(session, true, ErrorCode.InitOk, null);
if (session.getState() != AUTHENTD) {
session.newState(AUTHENTD);
}
localChannelReference.validateConnection(true, result);
logger.debug("Local Server Channel Validated: {} ",
localChannelReference != null? localChannelReference :
"no LocalChannelReference");
session.setStatus(44);
NetworkTransaction.addClient(
localChannelReference.getNetworkChannelObject(), packet.getHostId());
if (packet.isToValidate()) {
// only requested
packet.validate(session.getAuth().isSsl());
ChannelUtils.writeAbstractLocalPacket(localChannelReference, packet,
false);
session.setStatus(98);
}
// Checking if partner is able to reuse authentication
if (allowReusableAuthentication() &&
Configuration.configuration.getVersions()
.containsKey(session.getAuth().getUser()) &&
Configuration.configuration.getVersions()
.get(session.getAuth().getUser())
.supportReuseAuthentication()) {
localChannelReference.getNetworkChannel()
.attr(NetworkServerHandler.REUSABLE_AUTH_KEY)
.set(session.getAuth().clone());
}
logger.debug("Partner: {} from {}", localChannelReference.getPartner(),
Configuration.configuration.getVersions());
}
public final boolean allowReusableAuthentication() {
return !Configuration.configuration.isAuthentNoReuse();
}
public final boolean hasReusableAuthentication() {
return allowReusableAuthentication() &&
localChannelReference.getNetworkChannel().hasAttr(
NetworkServerHandler.REUSABLE_AUTH_KEY) &&
localChannelReference.getNetworkChannel()
.attr(NetworkServerHandler.REUSABLE_AUTH_KEY)
.get() != null;
}
public final boolean validateAuthenticationReuse()
throws OpenR66ProtocolNotAuthenticatedException {
if (hasReusableAuthentication()) {
// Already authenticated
final R66Auth source = localChannelReference.getNetworkChannel().attr(
NetworkServerHandler.REUSABLE_AUTH_KEY).get();
if (source != null) {
if (localChannelReference.getDbSession() != null) {
localChannelReference.getDbSession().useConnection();
}
if (localChannelReference.getNetworkChannelObject() != null) {
localChannelReference.getNetworkChannelObject()
.setHostId(source.getUser());
}
localChannelReference.getSession().getAuth().setFromClone(source);
localChannelReference.setPartner(source.getUser());
if (session.getBusinessObject() != null) {
try {
session.getBusinessObject().checkAtAuthentication(session);
} catch (final OpenR66RunnerErrorException e) {
session.setStatus(104);
throw new OpenR66ProtocolNotAuthenticatedException(e.getMessage());
}
}
// Check compression
session.setCompressionEnabled(
localChannelReference.getPartner().isCompression() &&
Configuration.configuration.isCompressionAvailable());
final R66Result result =
new R66Result(session, true, ErrorCode.InitOk, null);
localChannelReference.validateConnection(true, result);
session.setStatus(44);
NetworkTransaction.addClient(
localChannelReference.getNetworkChannelObject(), source.getUser());
final R66FiniteDualStates state =
localChannelReference.getSessionState();
if (state == AUTHENTR || state == STARTUP) {
localChannelReference.sessionNewState(AUTHENTD);
}
logger.debug("Authentication is done using reuse");
return true;
}
}
logger.debug("Authentication will be done as usual");
return false;
}
/**
* Receive a connection error
*
* @param packet
*/
public final void connectionError(final ConnectionErrorPacket packet) {
// do something according to the error
logger.error(localChannelReference.getRequestId() + ": " + packet);
ErrorCode code = ErrorCode.ConnectionImpossible;
if (packet.getSmiddle() != null) {
code = ErrorCode.getFromCode(packet.getSmiddle());
}
localChannelReference.invalidateRequest(
new R66Result(new OpenR66ProtocolSystemException(packet.getSheader()),
session, true, code, null));
// True since closing
session.newState(ERROR);
session.setStatus(45);
businessError();
localChannelReference.close();
}
/**
* Receive a remote error
*
* @param packet
*
* @throws OpenR66RunnerErrorException
* @throws OpenR66ProtocolSystemException
* @throws OpenR66ProtocolBusinessException
*/
public final void errorMesg(final ErrorPacket packet)
throws OpenR66RunnerErrorException, OpenR66ProtocolSystemException,
OpenR66ProtocolBusinessException {
// do something according to the error
if (session.getLocalChannelReference().getFutureRequest().isDone()) {
// already canceled or successful
return;
}
logger.error(localChannelReference.getRequestId() + ": " + packet);
session.setStatus(46);
final ErrorCode code = ErrorCode.getFromCode(packet.getSmiddle());
session.getLocalChannelReference()
.setErrorMessage(packet.getSheader(), code);
final OpenR66ProtocolBusinessException exception;
if (code.code == ErrorCode.CanceledTransfer.code) {
NetworkTransaction.stopRetrieve(session.getLocalChannelReference());
logger.info("Stop retrieving file: the transfer has been canceled");
exception =
new OpenR66ProtocolBusinessCancelException(packet.getSheader());
final int rank = 0;
final DbTaskRunner runner = session.getRunner();
if (runner != null) {
runner.setRankAtStartup(rank);
runner.stopOrCancelRunner(code);
}
final R66Result result =
new R66Result(exception, session, true, code, runner);
// now try to inform other
session.setFinalizeTransfer(false, result);
try {
ChannelUtils.writeAbstractLocalPacket(localChannelReference, packet,
false).addListener(
new RunnerChannelFutureListener(localChannelReference, result));
} catch (final OpenR66ProtocolPacketException ignored) {
// ignore
}
return;
} else if (code.code == ErrorCode.StoppedTransfer.code) {
NetworkTransaction.stopRetrieve(session.getLocalChannelReference());
logger.info("Stop retrieving file: the transfer has been stopped");
exception = new OpenR66ProtocolBusinessStopException(packet.getSheader());
final String[] vars = packet.getSheader().split(" ");
final String var = vars[vars.length - 1];
final int rank = Integer.parseInt(var);
final DbTaskRunner runner = session.getRunner();
if (runner != null) {
if (rank < runner.getRank()) {
runner.setRankAtStartup(rank);
}
runner.stopOrCancelRunner(code);
}
final R66Result result =
new R66Result(exception, session, true, code, runner);
// now try to inform other
session.setFinalizeTransfer(false, result);
try {
ChannelUtils.writeAbstractLocalPacket(localChannelReference, packet,
false).addListener(
new RunnerChannelFutureListener(localChannelReference, result));
} catch (final OpenR66ProtocolPacketException ignored) {
// ignore
}
return;
} else if (code.code == ErrorCode.QueryAlreadyFinished.code) {
final DbTaskRunner runner = session.getRunner();
if (runner == null) {
exception =
new OpenR66ProtocolBusinessCancelException(packet.toString());
} else {
if (session.isSender()) {
exception = new OpenR66ProtocolBusinessQueryAlreadyFinishedException(
packet.getSheader());
runner.finishTransferTask(code);
tryFinalizeRequest(
new R66Result(exception, session, true, code, runner));
} else {
exception =
new OpenR66ProtocolBusinessCancelException(packet.toString());
}
}
throw exception;
} else if (code.code == ErrorCode.QueryStillRunning.code) {
exception = new OpenR66ProtocolBusinessQueryStillRunningException(
packet.getSheader());
throw exception;
} else if (code.code == ErrorCode.BadAuthent.code) {
exception =
new OpenR66ProtocolNotAuthenticatedException(packet.toString());
} else if (code.code == ErrorCode.QueryRemotelyUnknown.code) {
exception = new OpenR66ProtocolBusinessCancelException(packet.toString());
} else if (code.code == ErrorCode.FileNotFound.code) {
exception = new OpenR66ProtocolBusinessRemoteFileNotFoundException(
packet.toString());
} else {
exception =
new OpenR66ProtocolBusinessNoWriteBackException(packet.toString());
}
session.setFinalizeTransfer(false,
new R66Result(exception, session, true, code,
session.getRunner()));
throw exception;
}
/**
* Try to finalize the request if possible
*
* @param errorValue in case of Error
*
* @throws OpenR66ProtocolSystemException
* @throws OpenR66RunnerErrorException
*/
public final void tryFinalizeRequest(final R66Result errorValue)
throws OpenR66RunnerErrorException, OpenR66ProtocolSystemException {
session.tryFinalizeRequest(errorValue);
}
/**
* Class to finalize a runner when the future is over
*/
private static final class RunnerChannelFutureListener
implements ChannelFutureListener {
private final LocalChannelReference localChannelReference;
private final R66Result result;
private RunnerChannelFutureListener(
final LocalChannelReference localChannelReference,
final R66Result result) {
this.localChannelReference = localChannelReference;
this.result = result;
}
@Override
public final void operationComplete(final ChannelFuture future) {
localChannelReference.invalidateRequest(result);
ChannelCloseTimer.closeFutureTransaction(
localChannelReference.getServerHandler());
}
}
}