/* * 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.dc; import org.dcache.ftp.client.Buffer; import org.dcache.ftp.client.DataSink; import org.dcache.ftp.client.vanilla.FTPServerFacade; import org.dcache.ftp.client.vanilla.BasicServerControlChannel; import java.io.IOException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Implements incoming transfer. * While the transfer is in progress, replies are sent to the * local control channel. Also any failure messages go there * in the form of a negative reply. **/ public class TransferSinkThread extends TransferThread { protected static final Logger logger = LoggerFactory.getLogger(TransferSinkThread.class); protected DataChannelReader reader; protected final DataSink sink; protected final BasicServerControlChannel localControlChannel; protected final TransferContext context; protected final SocketBox socketBox; public TransferSinkThread(AbstractDataChannel dataChannel, SocketBox socketBox, DataSink sink, BasicServerControlChannel localControlChannel, TransferContext context) throws Exception { this.socketBox = socketBox; this.sink = sink; this.localControlChannel = localControlChannel; this.context = context; this.reader = dataChannel.getDataChannelSource(context); reader.setDataStream(socketBox.getSocket().getInputStream()); } @Override public void run() { boolean error = false; Object quitToken = null; logger.debug("TransferSinkThread executing"); try { startup(); try { copy(); } catch (Exception e) { error = true; FTPServerFacade.exceptionToControlChannel( e, "exception during TransferSinkThread", localControlChannel); } finally { // attempt to obtain permission to close resources quitToken = context.getQuitToken(); shutdown(quitToken); } if (!error) { // local control channel is shared by all data channels // so only the last one exiting may send "226 transfer complete" if (quitToken != null) { localControlChannel.write(new LocalReply(226)); } } } catch (Exception e) { // exception occurred when trying to write to local // control channel. So there is no way to inform // the user. FTPServerFacade.cannotPropagateError(e); } } protected void startup() throws Exception { //send initial reply only if nothing has yet been sent synchronized (localControlChannel) { if (localControlChannel.getReplyCount() == 0) { // 125 Data connection already open; transfer starting localControlChannel.write(new LocalReply(125)); } } } protected void copy() throws Exception { Buffer buf; long transferred = 0; while ((buf = reader.read()) != null) { transferred += buf.getLength(); sink.write(buf); } logger.debug("finished receiving data; received " + transferred + " bytes"); } protected void shutdown(Object quitToken) throws IOException { logger.debug("shutdown"); reader.close(); // garbage collect the socket socketBox.setSocket(null); // data sink is shared by all data channels, // so should be closed by the last one exiting if (quitToken != null) { sink.close(); } } }