/** * 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.localhandler; import io.netty.channel.Channel; import io.netty.channel.local.LocalChannel; import io.netty.handler.traffic.ChannelTrafficShapingHandler; import org.waarp.common.database.DbSession; import org.waarp.common.database.exception.WaarpDatabaseNoConnectionException; import org.waarp.common.logging.WaarpLogger; import org.waarp.common.logging.WaarpLoggerFactory; import org.waarp.openr66.client.RecvThroughHandler; 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.task.exception.OpenR66RunnerErrorException; import org.waarp.openr66.database.DbConstant; import org.waarp.openr66.database.data.DbTaskRunner; import org.waarp.openr66.protocol.configuration.Configuration; import org.waarp.openr66.protocol.configuration.PartnerConfiguration; import org.waarp.openr66.protocol.exception.OpenR66Exception; import org.waarp.openr66.protocol.exception.OpenR66ProtocolNoConnectionException; import org.waarp.openr66.protocol.exception.OpenR66ProtocolRemoteShutdownException; import org.waarp.openr66.protocol.networkhandler.NetworkChannelReference; import org.waarp.openr66.protocol.networkhandler.NetworkServerHandler; import org.waarp.openr66.protocol.networkhandler.NetworkServerInitializer; import org.waarp.openr66.protocol.networkhandler.NetworkTransaction; import org.waarp.openr66.protocol.utils.R66Future; import org.waarp.openr66.protocol.utils.R66Versions; /** * Reference of one object using Local Channel localId and containing local channel and network * channel. * * @author Frederic Bregier */ public class LocalChannelReference { /** * Internal Logger */ private static final WaarpLogger logger = WaarpLoggerFactory.getLogger(LocalChannelReference.class); /** * Local Channel */ private final LocalChannel localChannel; /** * Network Channel Ref */ private final NetworkChannelReference networkChannelRef; /** * Traffic handler associated if any */ private final ChannelTrafficShapingHandler cts; /** * Network Server Handler */ private final NetworkServerHandler networkServerHandler; /** * Local Id */ private final Integer localId; /** * Remote Id */ private Integer remoteId; /** * Requested_requester_specialId */ private String requestId; /** * Future on Request */ private final R66Future futureRequest; /** * Future on Valid Starting Request */ private final R66Future futureValidRequest = new R66Future(true); /** * Future on Transfer */ private R66Future futureEndTransfer = new R66Future(true); /** * Future on Connection */ private final R66Future futureConnection = new R66Future(true); /** * Future on Startup */ private final R66Future futureStartup = new R66Future(true); /** * Session */ private R66Session session; /** * Last error message */ private String errorMessage = "NoError"; /** * Last error code */ private ErrorCode code = ErrorCode.Unknown; /** * RecvThroughHandler */ private RecvThroughHandler recvThroughHandler; private boolean isSendThroughMode = false; /** * Thread for ClientRunner if any */ private ClientRunner clientRunner = null; /** * To be able to check hash once all transfer is over once again */ private String hashComputeDuringTransfer = null; /** * If partial hash, no global hash validation can be done */ private boolean partialHash = false; /** * PartnerConfiguration */ private volatile PartnerConfiguration partner; /** * DbSession for Database that do not support concurrency in access */ private volatile DbSession noconcurrencyDbSession = null; /** * * @param localChannel * @param networkChannelRef * @param remoteId * @param futureRequest * @throws OpenR66ProtocolRemoteShutdownException */ public LocalChannelReference(LocalChannel localChannel, NetworkChannelReference networkChannelRef, Integer remoteId, R66Future futureRequest) throws OpenR66ProtocolRemoteShutdownException { this.localChannel = localChannel; this.networkChannelRef = networkChannelRef; networkServerHandler = (NetworkServerHandler) this.networkChannelRef.channel().pipeline().last(); localId = this.localChannel.id().hashCode(); this.remoteId = remoteId; if (futureRequest == null) { this.futureRequest = new R66Future(true); } else { if (futureRequest.isDone()) { futureRequest.reset(); } this.futureRequest = futureRequest; } cts = (ChannelTrafficShapingHandler) networkChannelRef.channel().pipeline() .get(NetworkServerInitializer.LIMITCHANNEL); if (DbConstant.admin.isActive() && !DbConstant.admin.isCompatibleWithThreadSharedConnexion()) { try { this.noconcurrencyDbSession = new DbSession(DbConstant.admin, false); } catch (WaarpDatabaseNoConnectionException e) { // Cannot connect so use default connection logger.warn("Use default database connection"); this.noconcurrencyDbSession = null; } } else { this.noconcurrencyDbSession = null; } networkChannelRef.add(this); } /** * Special empty LCR constructor */ public LocalChannelReference() { this.localChannel = null; this.networkChannelRef = null; networkServerHandler = null; localId = 0; this.futureRequest = new R66Future(true); cts = null; } /** * Close the localChannelReference */ public void close() { Configuration.configuration.getLocalTransaction().remove(this); // Now force the close of the database after a wait if (noconcurrencyDbSession != null && DbConstant.admin != null && DbConstant.admin.getSession() != null && !noconcurrencyDbSession.equals(DbConstant.admin.getSession())) { noconcurrencyDbSession.forceDisconnect(); noconcurrencyDbSession = null; } } /** * @return the localChannel */ public LocalChannel getLocalChannel() { return localChannel; } /** * @return the networkChannelRef */ public Channel getNetworkChannel() { return networkChannelRef.channel(); } /** * @return the id */ public Integer getLocalId() { return localId; } /** * @return the remoteId */ public Integer getRemoteId() { return remoteId; } /** * @return the ChannelTrafficShapingHandler */ public ChannelTrafficShapingHandler getChannelTrafficShapingHandler() { return cts; } /** * @return the networkChannelObject */ public NetworkChannelReference getNetworkChannelObject() { return networkChannelRef; } /** * @return the networkServerHandler */ public NetworkServerHandler getNetworkServerHandler() { return networkServerHandler; } /** * * @return the actual dbSession */ public DbSession getDbSession() { if (noconcurrencyDbSession != null) { return noconcurrencyDbSession; } if (networkServerHandler != null) { return networkServerHandler.getDbSession(); } logger.info("SHOULD NOT BE"); return DbConstant.admin.getSession(); } /** * @param remoteId * the remoteId to set */ public void setRemoteId(Integer remoteId) { this.remoteId = remoteId; } /** * @return the session */ public R66Session getSession() { return session; } /** * @param session * the session to set */ public void setSession(R66Session session) { this.session = session; } /** * @return the current errorMessage */ public String getErrorMessage() { return errorMessage; } /** * @param errorMessage * the errorMessage to set */ public void setErrorMessage(String errorMessage, ErrorCode code) { this.errorMessage = errorMessage; this.code = code; } /** * @return the code */ public ErrorCode getCurrentCode() { return code; } /** * Validate or not the Startup (before connection) * * @param validate */ public void validateStartup(boolean validate) { if (futureStartup.isDone()) { return; } if (validate) { futureStartup.setSuccess(); } else { futureStartup.cancel(); } } /** * * @return the futureValidateStartup */ public R66Future getFutureValidateStartup() { try { if (!futureStartup.await(Configuration.configuration.getTIMEOUTCON())) { validateStartup(false); return futureStartup; } } catch (InterruptedException e) { validateStartup(false); return futureStartup; } return futureStartup; } /** * * @return True if the connection is validated (in OK or KO status) */ public boolean isConnectionValidate() { return futureConnection.isDone(); } /** * Validate or Invalidate the connection (authentication) * * @param validate */ public void validateConnection(boolean validate, R66Result result) { if (futureConnection.isDone()) { logger.debug("LocalChannelReference already validated: " + futureConnection.isSuccess()); return; } if (validate) { futureConnection.setResult(result); futureConnection.setSuccess(); } else { futureConnection.setResult(result); setErrorMessage(result.getMessage(), result.getCode()); futureConnection.cancel(); } } /** * * @return the futureValidateConnection */ public R66Future getFutureValidateConnection() { R66Result result; try { for (int i = 0; i < Configuration.RETRYNB; i++) { Channel channel = this.networkChannelRef.channel(); if (channel != null && channel.isActive()) { if (!futureConnection.await(Configuration.configuration.getTIMEOUTCON())) { if (futureConnection.isDone()) { return futureConnection; } else { if (channel.isActive()) { continue; } result = new R66Result( new OpenR66ProtocolNoConnectionException( "Out of time"), session, false, ErrorCode.ConnectionImpossible, null); validateConnection(false, result); return futureConnection; } } else { return futureConnection; } } else { break; } } } catch (InterruptedException e) { result = new R66Result( new OpenR66ProtocolNoConnectionException( "Interrupted connection"), session, false, ErrorCode.ConnectionImpossible, null); validateConnection(false, result); return futureConnection; } logger.info("Cannot get Connection due to out of Time: {}", this); result = new R66Result( new OpenR66ProtocolNoConnectionException( "Out of time"), session, false, ErrorCode.ConnectionImpossible, null); validateConnection(false, result); return futureConnection; } /** * Validate the End of a Transfer * * @param finalValue */ public void validateEndTransfer(R66Result finalValue) { if (!futureEndTransfer.isDone()) { futureEndTransfer.setResult(finalValue); futureEndTransfer.setSuccess(); } else { logger.debug("Could not validate since Already validated: " + futureEndTransfer.isSuccess() + " " + finalValue); if (!futureEndTransfer.getResult().isAnswered()) { futureEndTransfer.getResult().setAnswered(finalValue.isAnswered()); } } } /** * @return the futureEndTransfer */ public R66Future getFutureEndTransfer() { return futureEndTransfer; } /** * Special waiter for Send Through method. It reset the EndTransfer future. * * @throws OpenR66Exception */ public void waitReadyForSendThrough() throws OpenR66Exception { logger.debug("Wait for End of Prepare Transfer"); try { this.futureEndTransfer.await(); } catch (InterruptedException e) { throw new OpenR66RunnerErrorException("Interrupted", e); } if (this.futureEndTransfer.isSuccess()) { // reset since transfer will start now this.futureEndTransfer = new R66Future(true); } else { throw this.futureEndTransfer.getResult().getException(); } } /** * @return the futureValidRequest */ public R66Future getFutureValidRequest() { return futureValidRequest; } /** * @return the futureRequest */ public R66Future getFutureRequest() { return futureRequest; } /** * Invalidate the current request * * @param finalvalue */ public void invalidateRequest(R66Result finalvalue) { R66Result finalValue = finalvalue; if (finalValue == null) { finalValue = new R66Result(session, false, ErrorCode.Unknown, this.session.getRunner()); } logger.debug("FET: " + futureEndTransfer.isDone() + ":" + futureEndTransfer.isSuccess() + " FVR: " + futureValidRequest.isDone() + ":" + futureValidRequest.isSuccess() + " FR: " + futureRequest.isDone() + ":" + futureRequest.isSuccess() + " " + finalValue.getMessage()); if (!futureEndTransfer.isDone()) { futureEndTransfer.setResult(finalValue); if (finalValue.getException() != null) { futureEndTransfer.setFailure(finalValue.getException()); } else { futureEndTransfer.cancel(); } } if (!futureValidRequest.isDone()) { futureValidRequest.setResult(finalValue); if (finalValue.getException() != null) { futureValidRequest.setFailure(finalValue.getException()); } else { futureValidRequest.cancel(); } } logger.debug("Invalidate Request", new Exception( "Trace for Invalidation")); if (finalValue.getCode() != ErrorCode.ServerOverloaded) { if (!futureRequest.isDone()) { setErrorMessage(finalValue.getMessage(), finalValue.getCode()); futureRequest.setResult(finalValue); if (finalValue.getException() != null) { futureRequest.setFailure(finalValue.getException()); } else { futureRequest.cancel(); } } else { logger.debug("Could not invalidate since Already finished: " + futureEndTransfer.getResult()); } } else { setErrorMessage(finalValue.getMessage(), finalValue.getCode()); logger.debug("Overloaded"); } if (this.session != null) { DbTaskRunner runner = this.session.getRunner(); if (runner != null) { if (runner.isSender()) { NetworkTransaction.stopRetrieve(this); } } } } /** * Validate the current Request * * @param finalValue */ public void validateRequest(R66Result finalValue) { setErrorMessage("NoError", null); if (!futureEndTransfer.isDone()) { logger.debug("Will validate EndTransfer"); validateEndTransfer(finalValue); } if (!futureValidRequest.isDone()) { futureValidRequest.setResult(finalValue); futureValidRequest.setSuccess(); } logger.debug("Validate Request"); if (!futureRequest.isDone()) { if (finalValue.getOther() == null && session.getBusinessObject() != null && session.getBusinessObject().getInfo(session) != null) { finalValue.setOther(session.getBusinessObject().getInfo(session)); } futureRequest.setResult(finalValue); futureRequest.setSuccess(); } else { logger.info("Already validated: " + futureRequest.isSuccess() + " " + finalValue); if (!futureRequest.getResult().isAnswered()) { futureRequest.getResult().setAnswered(finalValue.isAnswered()); } } } @Override public String toString() { return "LCR: L: " + localId + " R: " + remoteId + " Startup[" + (futureStartup != null ? futureStartup : "noStartup") + "] Conn[" + (futureConnection != null ? futureConnection : "noConn") + "] ValidRequestRequest[" + (futureValidRequest != null ? futureValidRequest : "noValidRequest") + "] EndTransfer[" + (futureEndTransfer != null ? futureEndTransfer : "noEndTransfer") + "] Request[" + (futureRequest != null ? futureRequest : "noRequest") + "]"; } /** * @return the recvThroughHandler */ public RecvThroughHandler getRecvThroughHandler() { return recvThroughHandler; } /** * * @return True if in RecvThrough Mode */ public boolean isRecvThroughMode() { return recvThroughHandler != null; } /** * @param recvThroughHandler * the recvThroughHandler to set */ public void setRecvThroughHandler(RecvThroughHandler recvThroughHandler) { this.recvThroughHandler = recvThroughHandler; } /** * @return True if in SendThrough Mode */ public boolean isSendThroughMode() { return isSendThroughMode; } /** * @param isSendThroughMode * the isSendThroughMode to set */ public void setSendThroughMode(boolean isSendThroughMode) { this.isSendThroughMode = isSendThroughMode; } /** * @return the clientRunner */ public ClientRunner getClientRunner() { return clientRunner; } /** * @param clientRunner * the clientRunner to set */ public void setClientRunner(ClientRunner clientRunner) { this.clientRunner = clientRunner; } /** * Shortcut to set a new state in Session * * @param desiredState */ public void sessionNewState(R66FiniteDualStates desiredState) { if (session != null) { session.newState(desiredState); } } /** * * @return the current state or TEST if no session exists */ public R66FiniteDualStates getSessionState() { if (session != null) { return session.getState(); } return R66FiniteDualStates.TEST; } /** * @return the hashComputeDuringTransfer */ public String getHashComputeDuringTransfer() { return hashComputeDuringTransfer; } /** * @param hashComputeDuringTransfer * the hashComputeDuringTransfer to set */ public void setHashComputeDuringTransfer(String hashComputeDuringTransfer) { this.hashComputeDuringTransfer = hashComputeDuringTransfer; } public void setPartialHash() { this.partialHash = true; } public boolean isPartialHash() { return this.partialHash; } /** * @return the partner */ public PartnerConfiguration getPartner() { return partner; } /** * @param hostId * the partner to set */ public void setPartner(String hostId) { logger.debug("host:" + hostId); partner = Configuration.configuration.getVersions().get(hostId); if (partner == null) { partner = new PartnerConfiguration(hostId, R66Versions.V2_4_12.getVersion()); } } /** * @return the requestId */ public String getRequestId() { return requestId; } /** * @param requestId * the requestId to set */ public void setRequestId(String requestId) { this.requestId = requestId; } }