/** * 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 java.util.concurrent.atomic.AtomicBoolean; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFuture; import io.netty.channel.local.LocalChannel; import org.waarp.common.file.DataBlock; import org.waarp.common.logging.WaarpLogger; import org.waarp.common.logging.WaarpLoggerFactory; 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.data.DbTaskRunner.TASKSTEP; import org.waarp.openr66.protocol.configuration.Configuration; import org.waarp.openr66.protocol.exception.OpenR66Exception; import org.waarp.openr66.protocol.exception.OpenR66ProtocolPacketException; import org.waarp.openr66.protocol.exception.OpenR66ProtocolSystemException; import org.waarp.openr66.protocol.localhandler.packet.EndRequestPacket; import org.waarp.openr66.protocol.localhandler.packet.ErrorPacket; import org.waarp.openr66.protocol.networkhandler.NetworkTransaction; import org.waarp.openr66.protocol.utils.ChannelUtils; /** * Retrieve transfer runner * * @author Frederic Bregier * */ public class RetrieveRunner extends Thread { /** * Internal Logger */ private static final WaarpLogger logger = WaarpLoggerFactory.getLogger(RetrieveRunner.class); private final R66Session session; private final LocalChannelReference localChannelReference; private final LocalChannel channel; private boolean done = false; protected AtomicBoolean running = new AtomicBoolean(true); protected RetrieveRunner() { // empty constructor this.session = null; this.localChannelReference = null; this.channel = null; } /** * * @param session * @param channel * local channel */ public RetrieveRunner(R66Session session, LocalChannel channel) { this.session = session; localChannelReference = this.session.getLocalChannelReference(); this.channel = channel; } /** * Try to stop the runner */ public void stopRunner() { running.set(false); } @Override public void run() { boolean requestValidDone = false; try { Thread.currentThread().setName("RetrieveRunner: " + channel.id()); try { if (session.getRunner().getGloballaststep() == TASKSTEP.POSTTASK.ordinal()) { logger.debug("Restart from POSTTASK: EndTransfer"); // restart from PostTask global step so just end now try { ChannelUtils.writeEndTransfer(localChannelReference); } catch (OpenR66ProtocolPacketException e) { transferInError(e); logger.error("End Retrieve in Error"); return; } } else { logger.debug("Start retrieve operation (send)"); session.getFile().retrieveBlocking(running); } } catch (OpenR66RunnerErrorException e) { transferInError(e); logger.info("End Retrieve in Error"); return; } catch (OpenR66ProtocolSystemException e) { transferInError(e); logger.info("End Retrieve in Error"); return; } if (running.get()) { try { localChannelReference.getFutureEndTransfer().await(); } catch (InterruptedException e1) { } } logger.debug("Await future End Transfer done: " + localChannelReference.getFutureEndTransfer().isSuccess()); if (localChannelReference.getFutureEndTransfer().isDone() && localChannelReference.getFutureEndTransfer().isSuccess()) { // send a validation requestValidDone = true; localChannelReference.sessionNewState(R66FiniteDualStates.ENDREQUESTS); EndRequestPacket validPacket = new EndRequestPacket(ErrorCode.CompleteOk.ordinal()); if (session.getExtendedProtocol() && session.getBusinessObject() != null && session.getBusinessObject().getInfo(session) != null) { validPacket.setOptional(session.getBusinessObject().getInfo(session)); } try { ChannelUtils.writeAbstractLocalPacket(localChannelReference, validPacket, true); } catch (OpenR66ProtocolPacketException e) { } if (!localChannelReference.getFutureRequest().awaitUninterruptibly( Configuration.configuration.getTIMEOUTCON())) { // valid it however session.getRunner().setAllDone(); try { session.getRunner().saveStatus(); } catch (OpenR66RunnerErrorException e) { // ignore } localChannelReference.validateRequest(localChannelReference .getFutureEndTransfer().getResult()); } if (session.getRunner() != null && session.getRunner().isSelfRequested()) { ChannelUtils.close(localChannelReference.getLocalChannel()); } done = true; } else { if (localChannelReference.getFutureEndTransfer().isDone()) { // Done and Not Success => error if (!localChannelReference.getFutureEndTransfer().getResult().isAnswered()) { localChannelReference.sessionNewState(R66FiniteDualStates.ERROR); ErrorPacket error = new ErrorPacket( localChannelReference.getErrorMessage(), localChannelReference.getFutureEndTransfer().getResult().getCode() .getCode(), ErrorPacket.FORWARDCLOSECODE); try { ChannelUtils.writeAbstractLocalPacket(localChannelReference, error, true); } catch (OpenR66ProtocolPacketException e) { } } } if (!localChannelReference.getFutureRequest().isDone()) { R66Result result = localChannelReference.getFutureEndTransfer().getResult(); if (result == null) { result = new R66Result(session, false, ErrorCode.TransferError, session.getRunner()); } localChannelReference.invalidateRequest(result); } done = true; logger.info("End Retrieve in Error"); } } finally { if (!done) { if (localChannelReference.getFutureEndTransfer().isDone() && localChannelReference.getFutureEndTransfer().isSuccess()) { if (!requestValidDone) { localChannelReference.sessionNewState(R66FiniteDualStates.ENDREQUESTS); EndRequestPacket validPacket = new EndRequestPacket( ErrorCode.CompleteOk.ordinal()); if (session.getExtendedProtocol() && session.getBusinessObject() != null && session.getBusinessObject().getInfo(session) != null) { validPacket.setOptional(session.getBusinessObject().getInfo(session)); } try { ChannelUtils.writeAbstractLocalPacket(localChannelReference, validPacket, true); } catch (OpenR66ProtocolPacketException e) { } } session.getRunner().setAllDone(); try { session.getRunner().saveStatus(); } catch (OpenR66RunnerErrorException e) { // ignore } localChannelReference.validateRequest(localChannelReference .getFutureEndTransfer().getResult()); if (session.getRunner() != null && session.getRunner().isSelfRequested()) { ChannelUtils.close(localChannelReference.getLocalChannel()); } } else { if (localChannelReference.getFutureEndTransfer().isDone()) { if (!localChannelReference.getFutureEndTransfer().getResult().isAnswered()) { localChannelReference.sessionNewState(R66FiniteDualStates.ERROR); ErrorPacket error = new ErrorPacket( localChannelReference.getErrorMessage(), localChannelReference.getFutureEndTransfer().getResult().getCode() .getCode(), ErrorPacket.FORWARDCLOSECODE); try { ChannelUtils.writeAbstractLocalPacket(localChannelReference, error, true); } catch (OpenR66ProtocolPacketException e) { } } } else { R66Result result = localChannelReference.getFutureEndTransfer().getResult(); if (result == null) { result = new R66Result(session, false, ErrorCode.TransferError, session.getRunner()); } localChannelReference.invalidateRequest(result); } } } NetworkTransaction.normalEndRetrieve(localChannelReference); } } private void transferInError(OpenR66Exception e) { R66Result result = new R66Result(e, session, true, ErrorCode.TransferError, session.getRunner()); logger.error("Transfer in error", e); session.newState(R66FiniteDualStates.ERROR); ErrorPacket error = new ErrorPacket("Transfer in error", ErrorCode.TransferError.getCode(), ErrorPacket.FORWARDCLOSECODE); try { ChannelUtils.writeAbstractLocalPacket(localChannelReference, error, true); } catch (OpenR66ProtocolPacketException e1) { } localChannelReference.invalidateRequest(result); ChannelUtils.close(channel); done = true; } /** * Write the next block when the channel is ready to prevent OOM * * @param block * @param localChannelReference * @return the ChannelFuture on the write operation * @throws OpenR66ProtocolPacketException * @throws OpenR66RunnerErrorException * @throws OpenR66ProtocolSystemException */ public static ChannelFuture writeWhenPossible( DataBlock block, LocalChannelReference localChannelReference) throws OpenR66ProtocolPacketException, OpenR66RunnerErrorException, OpenR66ProtocolSystemException { return ChannelUtils.writeBackDataBlock(localChannelReference, block); // XXX Keep this in case the bug comes back /* * // Test if channel is writable in order to prevent OOM if (! * localChannelReference.getNetworkChannel().isWritable()) { return * ChannelUtils.writeBackDataBlock(localChannelReference, block); } else if * (Configuration.configuration.anyBandwidthLimitation) { // Patch to limit the impact when * no real reason to wait for writing // double computation of traffic but ok long wait = * ChannelUtils.willBeWaitingWriting(localChannelReference, block.getByteCount()); if (wait * == 0) { ChannelUtils.writeBackDataBlock(localChannelReference, block); return * Channels.succeededFuture(localChannelReference.getNetworkChannel()); } return * ChannelUtils.writeBackDataBlock(localChannelReference, block); } else { * ChannelUtils.writeBackDataBlock(localChannelReference, block); return * Channels.succeededFuture(localChannelReference.getNetworkChannel()); } */ } /** * Utility method for send through mode * * @param data * the data byte, if null it is the last block * @return the DataBlock associated to the data */ public static DataBlock transformToDataBlock(byte[] data) { DataBlock block = new DataBlock(); if (data == null) { // last block block.setEOF(true); } else { block.setBlock(Unpooled.wrappedBuffer(data)); } return block; } }