All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.globus.ftp.vanilla.FTPControlChannel Maven / Gradle / Ivy

The newest version!
/*
 * 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.globus.ftp.vanilla;

import java.net.Socket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketTimeoutException;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;

import org.globus.common.CoGProperties;
import org.globus.net.SocketFactory;
import org.globus.ftp.exception.ServerException;
import org.globus.ftp.exception.UnexpectedReplyCodeException;
import org.globus.ftp.exception.FTPReplyParseException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * 

* Represents FTP Protocol Interpreter. Encapsulates * control channel communication. * *

*/ public class FTPControlChannel extends BasicClientControlChannel { private static Log logger = LogFactory.getLog(FTPControlChannel.class.getName()); public static final String CRLF = "\r\n"; // used in blocking waitForReply() private static final int WAIT_FOREVER = -1; protected Socket socket; //input stream protected BufferedReader ftpIn; //raw stream underlying ftpIn protected InputStream rawFtpIn; //output stream protected OutputStream ftpOut; protected String host; protected int port; //true if connection has already been opened. protected boolean hasBeenOpened = false; private boolean ipv6 = false; private Reply lastReply; public FTPControlChannel(String host, int port) { this.host = host; this.port = port; this.ipv6 = (this.host.indexOf(':') != -1); } /** * Using this constructor, you can initialize an instance that does not * talk directly to the socket. If you use this constructor using streams * that belong to an active connection, there's no need to call open() * afterwards. **/ public FTPControlChannel(InputStream in, OutputStream out) { setInputStream(in); setOutputStream(out); } public String getHost() { return this.host; } public int getPort() { return this.port; } public boolean isIPv6() { return this.ipv6; } protected BufferedReader getBufferedReader() { return ftpIn; } protected OutputStream getOutputStream() { return ftpOut; } // not intended to be public. you can set streams in the constructor. protected void setInputStream(InputStream in) { rawFtpIn = in; ftpIn = new BufferedReader(new InputStreamReader(rawFtpIn)); } protected void setOutputStream(OutputStream out) { ftpOut = out; } /** * opens the connection and returns after it is ready for communication. * Before returning, it intercepts the initial server reply(-ies), * and not positive, throws UnexpectedReplyCodeException. * After returning, there should be no more queued replies on the line. * * Here's the sequence for connection establishment (rfc959): *
     *     120
     *         220
     *     220
     *     421
     *
* @throws IOException on I/O error * @throws ServerException on negative or faulty server reply **/ public void open() throws IOException, ServerException { if (hasBeenOpened()) { throw new IOException("Attempt to open an already opened connection"); } InetAddress allIPs[]; //depending on constructor used, we may already have streams if (!haveStreams()) { boolean found = false; int i = 0; boolean firstPass = true; allIPs = InetAddress.getAllByName(host); while(!found) { try { logger.debug("opening control channel to " + allIPs[i] + " : " + port); InetSocketAddress isa = new InetSocketAddress(allIPs[i], port); socket = new Socket(); socket.setSoTimeout(CoGProperties.getDefault().getSocketTimeout()); socket.connect(isa, CoGProperties.getDefault().getSocketTimeout()); found = true; } catch(IOException ioEx) { logger.debug("failed connecting to " + allIPs[i] + " : " + port +":"+ioEx); i++; if(i == allIPs.length) { if(firstPass) { firstPass = false; i = 0; } else { throw ioEx; } } } } String pv = System.getProperty("org.globus.ftp.IPNAME"); if(pv != null) { host = socket.getInetAddress().getHostAddress(); } else { host = socket.getInetAddress().getCanonicalHostName(); } setInputStream(socket.getInputStream()); setOutputStream(socket.getOutputStream()); } readInitialReplies(); hasBeenOpened = true; } //intercepts the initial replies //(that the server sends after opening control ch.) protected void readInitialReplies() throws IOException, ServerException { Reply reply = null; try { reply = read(); } catch (FTPReplyParseException rpe) { throw ServerException.embedFTPReplyParseException( rpe, "Received faulty initial reply"); } if (Reply.isPositivePreliminary(reply)) { try { reply = read(); } catch (FTPReplyParseException rpe) { throw ServerException.embedFTPReplyParseException( rpe, "Received faulty second reply"); } } if (!Reply.isPositiveCompletion(reply)) { close(); throw ServerException.embedUnexpectedReplyCodeException( new UnexpectedReplyCodeException(reply), "Server refused connection."); } } /** * Returns the last reply received from the server. */ public Reply getLastReply() { return lastReply; } /** * Closes the control channel */ public void close() throws IOException { logger.debug("ftp socket closed"); if (ftpIn != null) ftpIn.close(); if (ftpOut != null) ftpOut.close(); if (socket != null) socket.close(); hasBeenOpened = false; } private int checkSocketDone(Flag aborted, int ioDelay, int maxWait) throws ServerException, IOException, InterruptedException { int oldTOValue = this.socket.getSoTimeout(); int c = -10; int time = 0; boolean done = false; if (ioDelay <= 0) { ioDelay = 2000; } while(!done) { try { if (aborted.flag) { throw new InterruptedException(); } this.socket.setSoTimeout(ioDelay); ftpIn.mark(2); c = ftpIn.read(); done = true; } catch (SocketTimeoutException e) { // timeouts will happen logger.debug("temp timeout" + e); } catch (Exception e) { throw new InterruptedException(); } finally { ftpIn.reset(); this.socket.setSoTimeout(oldTOValue); } time += ioDelay; if(time > maxWait && maxWait != WAIT_FOREVER) { throw new ServerException(ServerException.REPLY_TIMEOUT); } } return c; } /** Block until one of the conditions are true:
  1. a reply is available in the control channel,
  2. timeout (maxWait) expired
  3. aborted flag changes to true.
If maxWait == WAIT_FOREVER, never timeout and only check conditions (1) and (3). @param maxWait timeout in miliseconds @param ioDelay frequency of polling the control channel and checking the conditions @param aborted flag indicating wait aborted. **/ public void waitFor(Flag aborted, int ioDelay, int maxWait) throws ServerException, IOException, InterruptedException { int oldTimeout = this.socket.getSoTimeout(); try { int c = 0; if (maxWait != WAIT_FOREVER) { this.socket.setSoTimeout(maxWait); } else { this.socket.setSoTimeout(0); } c = this.checkSocketDone(aborted, ioDelay, maxWait); /* A bug in the server causes it to append \0 to each reply. As the result, we receive this \0 before the next reply. The code below handles this case. */ if (c != 0) { // if we're here, the server is healthy // and the reply is waiting in the buffer return; } // if we're here, we deal with the buggy server. // we discarded the \0 and now resume wait. logger.debug("Server sent \\0; resume wait"); try { // gotta read past the 0 we just remarked c = ftpIn.read(); c = this.checkSocketDone(aborted, ioDelay, maxWait); } catch (SocketTimeoutException e) { throw new ServerException(ServerException.REPLY_TIMEOUT); } catch (EOFException e) { throw new InterruptedException(); } } finally { this.socket.setSoTimeout(oldTimeout); } } /** * Block until a reply is available in the control channel. * @return the first unread reply from the control channel. * @throws IOException on I/O error * @throws FTPReplyParseException on malformatted server reply **/ public Reply read() throws ServerException, IOException, FTPReplyParseException, EOFException { Reply reply = new Reply(ftpIn); //System.out.println("FTP IN string "+reply.toString()); if (logger.isDebugEnabled()) { logger.debug("Control channel received: " + reply); } lastReply = reply; return reply; } public void abortTransfer() { } /** * Sends the command over the control channel. * Do not wait for reply. * @throws java.io.IOException on I/O error * @param cmd FTP command */ public void write(Command cmd) throws IOException, IllegalArgumentException { //we delete the initial reply when the first command is sent if (cmd == null) { throw new IllegalArgumentException("null argument: cmd"); } if (logger.isDebugEnabled()) { logger.debug("Control channel sending: " + cmd); } writeStr(cmd.toString()); } /** * Write the command to the control channel, * block until reply arrives and return the reply. * Before calling this method make sure that no old replies are * waiting on the control channel. Otherwise the reply returned * may not be the reply to this command. * @throws java.io.IOException on I/O error * @throws FTPReplyParseException on bad reply format * @param cmd FTP command * @return the first reply that waits in the control channel **/ public Reply exchange(Command cmd) throws ServerException, IOException, FTPReplyParseException { // send the command write(cmd); // get the reply return read(); } /** * Write the command to the control channel, * block until reply arrives and check if the command * completed successfully (reply code 200). * If so, return the reply, otherwise throw exception. * Before calling this method make sure that no old replies are * waiting on the control channel. Otherwise the reply returned * may not be the reply to this command. * @throws java.io.IOException on I/O error * @throws FTPReplyParseException on bad reply format * @throws UnexpectedReplyCodeException if reply is not a positive * completion reply (code 200) * @param cmd FTP command * @return the first reply that waits in the control channel **/ public Reply execute(Command cmd) throws ServerException, IOException, FTPReplyParseException, UnexpectedReplyCodeException { Reply reply = exchange(cmd); // check for positive reply if (!Reply.isPositiveCompletion(reply)) { throw new UnexpectedReplyCodeException(reply); } return reply; } protected void writeln(String msg) throws IOException { writeStr(msg + CRLF); } protected void writeStr(String msg) throws IOException { ftpOut.write(msg.getBytes()); ftpOut.flush(); } protected boolean hasBeenOpened() { return hasBeenOpened; } protected boolean haveStreams() { return (ftpIn != null && ftpOut != null); } } // end StandardPI




© 2015 - 2025 Weber Informatics LLC | Privacy Policy