/* * Copyright 1999-2006 University of Chicago * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.dcache.ftp.client.vanilla; import org.dcache.ftp.client.MarkerListener; import org.dcache.ftp.client.PerfMarker; import org.dcache.ftp.client.GridFTPRestartMarker; import org.dcache.ftp.client.exception.ServerException; import org.dcache.ftp.client.exception.UnexpectedReplyCodeException; import org.dcache.ftp.client.exception.FTPReplyParseException; import java.io.InterruptedIOException; import java.io.IOException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class TransferMonitor implements Runnable { public static final int LOCAL = 1, REMOTE = 2; private final int side; // source or dest private Logger logger = null; private final int maxWait; private final int ioDelay; private final BasicClientControlChannel controlChannel; private final TransferState transferState; private final MarkerListener mListener; private TransferMonitor other; private boolean abortable; private final Flag aborted = new Flag(); private Thread thread; public TransferMonitor(BasicClientControlChannel controlChannel, TransferState transferState, MarkerListener mListener, int maxWait, int ioDelay, int side) { logger = LoggerFactory.getLogger(TransferMonitor.class.getName() + ((side == LOCAL) ? ".Local" : ".Remote")); this.controlChannel = controlChannel; this.transferState = transferState; this.mListener = mListener; this.maxWait = maxWait; this.ioDelay = ioDelay; abortable = true; aborted.flag = false; this.side = side; } /** * In this class, each instance gets a separate logger which is * assigned the name in the constructor. * This name is in the form "...GridFTPClient.thread host:port". * * @return the logger name. **/ public String getLoggerName() { return logger.toString(); } public void setOther(TransferMonitor other) { this.other = other; } /** * Abort the tpt transfer * but do not close resources */ public synchronized void abort() { logger.debug("abort"); if (!this.abortable) { return; } controlChannel.abortTransfer(); aborted.flag = true; } private synchronized void done() { this.abortable = false; } public void start(boolean blocking) { if (blocking) { this.thread = Thread.currentThread(); run(); } else { this.thread = new Thread(this); this.thread.setName("TransferMonitor" + this.thread.getName()); this.thread.start(); } } @Override public void run() { try { // if the other thread had already terminated // with an error, behave as if it happened just now. if (transferState.hasError()) { logger.debug("the other thread terminated before this one started."); throw new InterruptedException(); } logger.debug("waiting for 1st reply; maxWait = " + maxWait + ", ioDelay = " + ioDelay); this.controlChannel.waitFor(aborted, ioDelay, maxWait); logger.debug("reading first reply"); Reply firstReply = controlChannel.read(); // 150 Opening BINARY mode data connection. // or // 125 Data connection already open; transfer starting if (Reply.isPositivePreliminary(firstReply)) { transferState.transferStarted(); logger.debug("first reply OK: " + firstReply.toString()); for (; ; ) { logger.debug("reading next reply"); this.controlChannel.waitFor(aborted, ioDelay); logger.debug("got next reply"); Reply nextReply = controlChannel.read(); //perf marker if (nextReply.getCode() == 112) { logger.debug("marker arrived: " + nextReply.toString()); if (mListener != null) { mListener.markerArrived( new PerfMarker(nextReply.getMessage())); } continue; } //restart marker if (nextReply.getCode() == 111) { logger.debug("marker arrived: " + nextReply.toString()); if (mListener != null) { mListener.markerArrived( new GridFTPRestartMarker( nextReply.getMessage())); } continue; } //226 Transfer complete if (nextReply.getCode() == 226) { abortable = false; logger.debug("transfer complete: " + nextReply.toString()); break; } // any other reply logger.debug("unexpected reply: " + nextReply.toString()); logger.debug("exiting the transfer thread"); ServerException e = ServerException.embedUnexpectedReplyCodeException( new UnexpectedReplyCodeException(nextReply), "Server reported transfer failure"); transferState.transferError(e); other.abort(); break; } } else { //first reply negative logger.debug("first reply bad: " + firstReply.toString()); logger.debug("category: " + firstReply.getCategory()); abortable = false; ServerException e = ServerException.embedUnexpectedReplyCodeException( new UnexpectedReplyCodeException(firstReply)); transferState.transferError(e); other.abort(); } logger.debug("thread dying naturally"); } catch (InterruptedException td) { //other transfer thread called abort() logger.debug("thread dying of InterruptedException."); transferState.transferError(td); } catch (InterruptedIOException td) { //other transfer thread called abort() which occurred //while this thread was performing IO logger.debug("thread dying of InterruptedIOException."); transferState.transferError(td); } catch (IOException e) { logger.debug("thread dying of IOException"); transferState.transferError(e); other.abort(); } catch (FTPReplyParseException rpe) { logger.debug("thread dying of FTPReplyParseException"); ServerException se = ServerException.embedFTPReplyParseException(rpe); transferState.transferError(se); other.abort(); } catch (ServerException e) { logger.debug("thread dying of timeout"); transferState.transferError(e); other.abort(); } finally { done(); transferState.transferDone(); } } }