/** * 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.commander; import java.net.SocketAddress; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import org.waarp.common.database.data.AbstractDbData; import org.waarp.common.database.data.AbstractDbData.UpdatedInfo; import org.waarp.common.database.exception.WaarpDatabaseException; import org.waarp.common.logging.WaarpLogger; import org.waarp.common.logging.WaarpLoggerFactory; import org.waarp.openr66.client.RecvThroughHandler; import org.waarp.openr66.context.ErrorCode; import org.waarp.openr66.context.R66FiniteDualStates; import org.waarp.openr66.context.R66Result; import org.waarp.openr66.context.authentication.R66Auth; import org.waarp.openr66.context.task.exception.OpenR66RunnerErrorException; import org.waarp.openr66.database.DbConstant; import org.waarp.openr66.database.data.DbHostAuth; 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.configuration.Messages; import org.waarp.openr66.protocol.exception.OpenR66ProtocolNoConnectionException; import org.waarp.openr66.protocol.exception.OpenR66ProtocolNotYetConnectionException; import org.waarp.openr66.protocol.exception.OpenR66ProtocolPacketException; import org.waarp.openr66.protocol.localhandler.LocalChannelReference; import org.waarp.openr66.protocol.localhandler.packet.RequestPacket; import org.waarp.openr66.protocol.networkhandler.NetworkTransaction; import org.waarp.openr66.protocol.utils.ChannelUtils; import org.waarp.openr66.protocol.utils.R66Future; import org.waarp.openr66.protocol.utils.TransferUtils; /** * Client Runner from a TaskRunner * * @author Frederic Bregier * */ public class ClientRunner extends Thread { /** * Internal Logger */ private static final WaarpLogger logger = WaarpLoggerFactory .getLogger(ClientRunner.class); private static final ConcurrentHashMap<String, Integer> taskRunnerRetryHashMap = new ConcurrentHashMap<String, Integer>(); public static ConcurrentLinkedQueue<ClientRunner> activeRunners = null; private final NetworkTransaction networkTransaction; private final DbTaskRunner taskRunner; private final R66Future futureRequest; private RecvThroughHandler handler = null; private boolean isSendThroughMode = false; private LocalChannelReference localChannelReference = null; public ClientRunner(NetworkTransaction networkTransaction, DbTaskRunner taskRunner, R66Future futureRequest) { this.networkTransaction = networkTransaction; this.taskRunner = taskRunner; this.futureRequest = futureRequest; } public static String hashStatus() { return "ClientRunner: [taskRunnerRetryHashMap: " + taskRunnerRetryHashMap.size() + " activeRunners: " + (activeRunners != null ? activeRunners.size() : 0) + "] "; } /** * @return the networkTransaction */ public NetworkTransaction getNetworkTransaction() { return networkTransaction; } /** * @return the taskRunner */ public DbTaskRunner getTaskRunner() { return taskRunner; } /** * @return the localChannelReference */ public LocalChannelReference getLocalChannelReference() { return localChannelReference; } @Override public void run() { if (Configuration.configuration.isShutdown()) { taskRunner.changeUpdatedInfo(UpdatedInfo.TOSUBMIT); taskRunner.forceSaveStatus(); return; } try { if (activeRunners != null) { activeRunners.add(this); } // fix for SelfRequest if (taskRunner.isSelfRequest()) { taskRunner.setSenderByRequestToValidate(false); } R66Future transfer; try { transfer = this.runTransfer(); } catch (OpenR66RunnerErrorException e) { logger.error("Runner Error: {} {}", e.getMessage(), taskRunner.toShortString()); return; } catch (OpenR66ProtocolNoConnectionException e) { logger.error("No connection Error {}", e.getMessage()); if (localChannelReference != null) { localChannelReference.setErrorMessage( ErrorCode.ConnectionImpossible.mesg, ErrorCode.ConnectionImpossible); } taskRunner.setErrorTask(localChannelReference); try { taskRunner.forceSaveStatus(); taskRunner.run(); } catch (OpenR66RunnerErrorException e1) { this.changeUpdatedInfo(UpdatedInfo.INERROR, ErrorCode.ConnectionImpossible, true); } return; } catch (OpenR66ProtocolPacketException e) { logger.error("Protocol Error", e); return; } catch (OpenR66ProtocolNotYetConnectionException e) { logger.warn("No connection warning {}", e.getMessage()); return; } R66Result result = transfer.getResult(); if (result != null) { if (result.getCode() == ErrorCode.QueryAlreadyFinished) { logger.warn(Messages.getString("Transfer.Status") + (transfer.isSuccess() ? Messages.getString("RequestInformation.Success") : Messages .getString("RequestInformation.Failure")) + " " + ErrorCode.QueryAlreadyFinished.mesg + ":" + (result != null ? result.toString() : "no result")); } else { if (transfer.isSuccess()) { logger.info(Messages.getString("Transfer.Status") + Messages.getString("RequestInformation.Success") + " " + (result != null ? result.toString() : "no result")); } else { logger.error(Messages.getString("Transfer.Status") + Messages.getString("RequestInformation.Failure") + " " + (result != null ? result.toString() : "no result")); } } } else { if (transfer.isSuccess()) { logger.warn(Messages.getString("Transfer.Status") + Messages.getString("RequestInformation.Success") + " no result"); } else { logger.error(Messages.getString("Transfer.Status") + Messages.getString("RequestInformation.Failure") + " no result"); } } transfer = null; Thread.currentThread().setName( "Finished_" + Thread.currentThread().getName()); } finally { if (activeRunners != null) { activeRunners.remove(this); } } } /** * * @param runner * @param limit * @return True if the task was run less than limit, else False */ public boolean incrementTaskRunnerTry(DbTaskRunner runner, int limit) { String key = runner.getKey(); Integer tries = taskRunnerRetryHashMap.get(key); logger.debug("try to find integer: " + tries); if (tries == null) { tries = Integer.valueOf(1); } else { tries = tries + 1; } if (limit <= tries) { taskRunnerRetryHashMap.remove(key); return false; } else { taskRunnerRetryHashMap.put(key, tries); return true; } } /** * True transfer run (can be called directly to enable exception outside any executors) * * @return The R66Future of the transfer operation * @throws OpenR66RunnerErrorException * @throws OpenR66ProtocolNoConnectionException * @throws OpenR66ProtocolPacketException * @throws OpenR66ProtocolNotYetConnectionException */ public R66Future runTransfer() throws OpenR66RunnerErrorException, OpenR66ProtocolNoConnectionException, OpenR66ProtocolPacketException, OpenR66ProtocolNotYetConnectionException { logger.debug("Start attempt Transfer"); localChannelReference = initRequest(); try { localChannelReference.getFutureValidRequest().await(); } catch (InterruptedException e) { } if (localChannelReference.getFutureValidRequest().isSuccess()) { return finishTransfer(localChannelReference); } else if (localChannelReference.getFutureValidRequest().getResult() != null && localChannelReference.getFutureValidRequest().getResult().getCode() == ErrorCode.ServerOverloaded) { return tryAgainTransferOnOverloaded(true, localChannelReference); } else return finishTransfer(localChannelReference); } /** * In case an overloaded signal is returned by the requested * * @param retry * if True, it will retry in case of overloaded remote server, else it just stops * @param localChannelReference * @return The R66Future of the transfer operation * @throws OpenR66RunnerErrorException * @throws OpenR66ProtocolNoConnectionException * @throws OpenR66ProtocolPacketException * @throws OpenR66ProtocolNotYetConnectionException */ public R66Future tryAgainTransferOnOverloaded(boolean retry, LocalChannelReference localChannelReference) throws OpenR66RunnerErrorException, OpenR66ProtocolNoConnectionException, OpenR66ProtocolPacketException, OpenR66ProtocolNotYetConnectionException { if (this.localChannelReference == null) { this.localChannelReference = localChannelReference; } boolean incRetry = incrementTaskRunnerTry(taskRunner, Configuration.RETRYNB); logger.debug("tryAgainTransferOnOverloaded: " + retry + ":" + incRetry); switch (taskRunner.getUpdatedInfo()) { case DONE: case INERROR: case INTERRUPTED: break; default: this.changeUpdatedInfo(UpdatedInfo.INERROR, ErrorCode.ServerOverloaded, true); } // redo if possible if (retry && incRetry) { try { Thread.sleep(Configuration.configuration.getConstraintLimitHandler() .getSleepTime()); } catch (InterruptedException e) { } return runTransfer(); } else { if (localChannelReference == null) { taskRunner .setLocalChannelReference(new LocalChannelReference()); } taskRunner.getLocalChannelReference().setErrorMessage( ErrorCode.ConnectionImpossible.mesg, ErrorCode.ConnectionImpossible); this.taskRunner.setErrorTask(localChannelReference); this.taskRunner.run(); throw new OpenR66ProtocolNoConnectionException( "End of retry on ServerOverloaded"); } } /** * Finish the transfer (called at the end of runTransfer) * * @param localChannelReference * @return The R66Future of the transfer operation * @throws OpenR66ProtocolNotYetConnectionException * @throws OpenR66ProtocolPacketException * @throws OpenR66ProtocolNoConnectionException * @throws OpenR66RunnerErrorException */ public R66Future finishTransfer(LocalChannelReference localChannelReference) throws OpenR66RunnerErrorException { if (this.localChannelReference == null) { this.localChannelReference = localChannelReference; } R66Future transfer = localChannelReference.getFutureRequest(); try { transfer.await(); } catch (InterruptedException e1) { } taskRunnerRetryHashMap.remove(taskRunner.getKey()); logger.info("Request done with {}", (transfer.isSuccess() ? "success" : "error")); localChannelReference.getLocalChannel().close(); // now reload TaskRunner if it still exists (light client can forget it) boolean isSender = taskRunner.isSender(); if (transfer.isSuccess()) { try { taskRunner.select(); } catch (WaarpDatabaseException e) { logger.debug("Not a problem but cannot find at the end the task"); taskRunner.setFrom(transfer.getRunner()); } taskRunner.setSender(isSender); this.changeUpdatedInfo(UpdatedInfo.DONE, ErrorCode.CompleteOk, false); } else { try { taskRunner.select(); } catch (WaarpDatabaseException e) { logger.debug("Not a problem but cannot find at the end the task"); taskRunner.setFrom(transfer.getRunner()); } taskRunner.setSender(isSender); // Case when we were interrupted if (transfer.getResult() == null) { switch (taskRunner.getUpdatedInfo()) { case DONE: R66Result ok = new R66Result(null, true, ErrorCode.CompleteOk, taskRunner); transfer.setResult(ok); transfer.setSuccess(); this.changeUpdatedInfo(UpdatedInfo.DONE, ErrorCode.CompleteOk, false); break; case INERROR: case INTERRUPTED: default: R66Result error = new R66Result(null, true, ErrorCode.Internal, taskRunner); transfer.setResult(error); transfer.cancel(); this.changeUpdatedInfo(UpdatedInfo.INERROR, ErrorCode.Internal, false); } return transfer; } if (transfer.getResult().getCode() == ErrorCode.QueryAlreadyFinished) { // check if post task to execute logger.warn("WARN QueryAlreadyFinished: " + transfer.toString() + " " + taskRunner.toShortString()); try { TransferUtils.finalizeTaskWithNoSession(taskRunner, localChannelReference); } catch (OpenR66RunnerErrorException e) { this.taskRunner.changeUpdatedInfo(UpdatedInfo.INERROR); this.taskRunner.forceSaveStatus(); } } else { switch (taskRunner.getUpdatedInfo()) { case DONE: case INERROR: case INTERRUPTED: case TOSUBMIT: break; default: this.changeUpdatedInfo(UpdatedInfo.INERROR, transfer.getResult().getCode(), false); } } } return transfer; } /** * Initialize the request * * @return the localChannelReference holding the transfer request * @throws OpenR66ProtocolNoConnectionException * @throws OpenR66RunnerErrorException * @throws OpenR66ProtocolPacketException * @throws OpenR66ProtocolNotYetConnectionException */ public LocalChannelReference initRequest() throws OpenR66ProtocolNoConnectionException, OpenR66RunnerErrorException, OpenR66ProtocolPacketException, OpenR66ProtocolNotYetConnectionException { this.changeUpdatedInfo(UpdatedInfo.RUNNING, ErrorCode.Running, true); long id = taskRunner.getSpecialId(); String tid; if (id == DbConstant.ILLEGALVALUE) { tid = taskRunner.getRuleId() + "_" + taskRunner.getMode() + "_NEWTRANSFER"; } else { tid = taskRunner.getRuleId() + "_" + taskRunner.getMode() + "_" + id; } Thread.currentThread().setName(tid); logger.debug("Will run {}", this.taskRunner); boolean restartPost = false; if (taskRunner.getGloballaststep() == TASKSTEP.POSTTASK.ordinal()) { // Send a validation to requested if (!taskRunner.isSelfRequested()) { // restart restartPost = true; } } if (taskRunner.isSelfRequested()) { // Don't have to restart a task for itself (or should use requester) logger.warn("Requested host cannot initiate itself the request"); this.changeUpdatedInfo(UpdatedInfo.INERROR, ErrorCode.LoopSelfRequestedHost, true); throw new OpenR66ProtocolNoConnectionException( "Requested host cannot initiate itself the request"); } DbHostAuth host = R66Auth.getServerAuth(DbConstant.admin.getSession(), taskRunner.getRequested()); if (host == null) { logger.error("Requested host cannot be found: " + taskRunner.getRequested()); this.changeUpdatedInfo(UpdatedInfo.INERROR, ErrorCode.NotKnownHost, true); throw new OpenR66ProtocolNoConnectionException( "Requested host cannot be found " + taskRunner.getRequested()); } if (host.isClient()) { logger.debug("Cannot initiate a connection with a client: {}", host); this.changeUpdatedInfo(UpdatedInfo.INERROR, ErrorCode.ConnectionImpossible, true); throw new OpenR66ProtocolNoConnectionException( "Cannot connect to client " + host.toString()); } SocketAddress socketAddress = host.getSocketAddress(); boolean isSSL = host.isSsl(); LocalChannelReference localChannelReference = networkTransaction .createConnectionWithRetry(socketAddress, isSSL, futureRequest); taskRunner.setLocalChannelReference(localChannelReference); if (localChannelReference == null) { // propose to redo // See if reprogramming is ok (not too many tries) String retry; if (incrementTaskRunnerTry(taskRunner, Configuration.RETRYNB)) { logger.debug("Will retry since Cannot connect to {}", host); retry = " but will retry"; // now wait try { Thread.sleep(Configuration.configuration.getDelayRetry()); } catch (InterruptedException e) { logger.debug( "Will not retry since limit of connection attemtps is reached for {}", host); retry = " and retries limit is reached so stop here"; this.changeUpdatedInfo(UpdatedInfo.INERROR, ErrorCode.ConnectionImpossible, true); taskRunner .setLocalChannelReference(new LocalChannelReference()); throw new OpenR66ProtocolNoConnectionException( "Cannot connect to server " + host.toString() + retry); } this.changeUpdatedInfo(UpdatedInfo.TOSUBMIT, ErrorCode.ConnectionImpossible, true); throw new OpenR66ProtocolNotYetConnectionException( "Cannot connect to server " + host.toString() + retry); } else { logger.debug( "Will not retry since limit of connection attemtps is reached for {}", host); retry = " and retries limit is reached so stop here"; this.changeUpdatedInfo(UpdatedInfo.INERROR, ErrorCode.ConnectionImpossible, true); taskRunner .setLocalChannelReference(new LocalChannelReference()); // set this server as being in shutdown status NetworkTransaction.proposeShutdownNetworkChannel(socketAddress); throw new OpenR66ProtocolNoConnectionException( "Cannot connect to server " + host.toString() + retry); } } socketAddress = null; if (handler != null) { localChannelReference.setRecvThroughHandler(handler); } localChannelReference.setSendThroughMode(isSendThroughMode); if (restartPost) { RequestPacket request = taskRunner.getRequest(); logger.debug("Will send request {} ", request); localChannelReference.setClientRunner(this); localChannelReference.sessionNewState(R66FiniteDualStates.REQUESTR); try { ChannelUtils.writeAbstractLocalPacket(localChannelReference, request, true); } catch (OpenR66ProtocolPacketException e) { // propose to redo logger.warn("Cannot transfer request to " + host.toString()); this.changeUpdatedInfo(UpdatedInfo.INTERRUPTED, ErrorCode.Internal, true); localChannelReference.getLocalChannel().close(); localChannelReference = null; host = null; request = null; throw e; } logger.debug("Wait for request to {}", host); request = null; host = null; return localChannelReference; } // If Requester is NOT Sender, and if TransferTask then decrease now if // possible the rank if (!taskRunner.isSender() && (taskRunner.getGloballaststep() == TASKSTEP.TRANSFERTASK .ordinal())) { logger.debug( "Requester is not Sender so decrease if possible the rank {}", taskRunner); taskRunner.restartRank(); taskRunner.forceSaveStatus(); logger.debug( "Requester is not Sender so new rank is " + taskRunner.getRank() + " {}", taskRunner); } RequestPacket request = taskRunner.getRequest(); logger.debug("Will send request {} {}", request, localChannelReference); localChannelReference.setClientRunner(this); localChannelReference.sessionNewState(R66FiniteDualStates.REQUESTR); try { ChannelUtils.writeAbstractLocalPacket(localChannelReference, request, true); } catch (OpenR66ProtocolPacketException e) { // propose to redo logger.warn("Cannot transfer request to " + host.toString()); this.changeUpdatedInfo(UpdatedInfo.INTERRUPTED, ErrorCode.Internal, true); localChannelReference.getLocalChannel().close(); localChannelReference = null; host = null; request = null; throw e; } logger.debug("Wait for request to {}", host); request = null; host = null; return localChannelReference; } /** * Change the UpdatedInfo of the current runner * * @param info */ public void changeUpdatedInfo(AbstractDbData.UpdatedInfo info, ErrorCode code, boolean force) { this.taskRunner.changeUpdatedInfo(info); this.taskRunner.setErrorExecutionStatus(code); if (force) { this.taskRunner.forceSaveStatus(); } else { try { this.taskRunner.saveStatus(); } catch (OpenR66RunnerErrorException e) { } } } /** * @param handler * the handler to set */ public void setRecvThroughHandler(RecvThroughHandler handler) { this.handler = handler; } public void setSendThroughMode() { isSendThroughMode = true; } public boolean getSendThroughMode() { return isSendThroughMode; } }