You are viewing a plain text version of this content. The canonical link for it is here.
Posted to ftpserver-commits@incubator.apache.org by ng...@apache.org on 2007/01/20 16:11:04 UTC

svn commit: r498136 - /incubator/ftpserver/trunk/core/src/java/org/apache/ftpserver/FtpDataConnection.java

Author: ngn
Date: Sat Jan 20 08:11:03 2007
New Revision: 498136

URL: http://svn.apache.org/viewvc?view=rev&rev=498136
Log:
Refactored FtpDataConnection to take care of data transfer (was previsoulsy done in Connection).
This also hides the use of blocking sockets in FtpDataConnection and enables us to use MINA for the data connection in the future.

Added:
    incubator/ftpserver/trunk/core/src/java/org/apache/ftpserver/FtpDataConnection.java   (with props)

Added: incubator/ftpserver/trunk/core/src/java/org/apache/ftpserver/FtpDataConnection.java
URL: http://svn.apache.org/viewvc/incubator/ftpserver/trunk/core/src/java/org/apache/ftpserver/FtpDataConnection.java?view=auto&rev=498136
==============================================================================
--- incubator/ftpserver/trunk/core/src/java/org/apache/ftpserver/FtpDataConnection.java (added)
+++ incubator/ftpserver/trunk/core/src/java/org/apache/ftpserver/FtpDataConnection.java Sat Jan 20 08:11:03 2007
@@ -0,0 +1,240 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.ftpserver;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.net.Socket;
+import java.util.zip.DeflaterOutputStream;
+import java.util.zip.InflaterInputStream;
+
+import org.apache.ftpserver.ftplet.DataType;
+import org.apache.ftpserver.ftplet.FtpSession;
+import org.apache.ftpserver.interfaces.FtpServerContext;
+import org.apache.ftpserver.usermanager.TransferRateRequest;
+import org.apache.ftpserver.util.IoUtils;
+
+
+/**
+ * We can get the ftp data connection using this class.
+ * It uses either PORT or PASV command.
+ * 
+ * @author <a href="mailto:rana_b@yahoo.com">Rana Bhattacharyya</a>
+ */
+public
+class FtpDataConnection {
+    
+    private FtpSessionImpl session;
+    private Socket socket;
+    private FtpDataConnectionFactory factory;
+    
+    public FtpDataConnection(Socket socket, FtpSessionImpl session, FtpDataConnectionFactory factory) {
+        this.session = session;
+        this.socket = socket;
+        this.factory = factory;
+    }
+
+    
+    /**
+     * Get data input stream. The return value will never be null.
+     */
+    private InputStream getDataInputStream() throws IOException {
+        try {
+            
+            // get data socket
+            Socket dataSoc = socket;
+            if(dataSoc == null) {
+                throw new IOException("Cannot open data connection.");
+            }
+            
+            // create input stream
+            InputStream is = dataSoc.getInputStream();
+            if(factory.isZipMode()) {
+                is = new InflaterInputStream(is);
+            }
+            return is;
+        }
+        catch(IOException ex) {
+            factory.closeDataSocket();
+            throw ex;
+        }
+    }
+    
+    /**
+     * Get data output stream. The return value will never be null.
+     */
+    private OutputStream getDataOutputStream() throws IOException {
+        try {
+            
+            // get data socket
+            Socket dataSoc = socket;
+            if(dataSoc == null) {
+                throw new IOException("Cannot open data connection.");
+            }
+            
+            // create output stream
+            OutputStream os = dataSoc.getOutputStream();
+            if(factory.isZipMode()) {
+                os = new DeflaterOutputStream(os);
+            }
+            return os;
+        }
+        catch(IOException ex) {
+            factory.closeDataSocket();
+            throw ex;
+        }
+    }
+    
+    /**
+     * Transfer data.
+     */
+    public final long transferFromClient(OutputStream out) throws IOException {
+        TransferRateRequest transferRateRequest = new TransferRateRequest();
+        transferRateRequest = (TransferRateRequest) session.getUser().authorize(transferRateRequest);
+        int maxRate = 0;
+        if(transferRateRequest != null) {
+            maxRate = transferRateRequest.getMaxUploadRate();
+        }
+        
+        InputStream is = getDataInputStream();
+        try {
+            return transfer(is, out, maxRate);
+        } finally {
+            IoUtils.close(is);
+        }
+    }
+
+    public final long transferToClient(InputStream in) throws IOException {
+        TransferRateRequest transferRateRequest = new TransferRateRequest();
+        transferRateRequest = (TransferRateRequest) session.getUser().authorize(transferRateRequest);
+        int maxRate = 0;
+        if(transferRateRequest != null) {
+            maxRate = transferRateRequest.getMaxDownloadRate();
+        }
+        
+        OutputStream out = getDataOutputStream();
+        try {
+            return transfer(in, out, maxRate);
+        } finally {
+            IoUtils.close(out);
+        }
+    }
+    
+    public final void transferToClient(String str) throws IOException {
+        OutputStream out = getDataOutputStream();
+        Writer writer = null;
+        try {
+            writer = new OutputStreamWriter(out, "UTF-8");
+            writer.write(str);
+        } finally {
+            if(writer != null) {
+                writer.flush();
+            }
+            IoUtils.close(writer);
+        }
+        
+    }
+    
+    private final long transfer(InputStream in, OutputStream out, int maxRate) throws IOException {
+        long transferredSize = 0L;
+
+        boolean isAscii = session.getDataType() == DataType.ASCII;
+        long startTime = System.currentTimeMillis();
+        byte[] buff = new byte[4096];
+        
+        BufferedInputStream bis = null;
+        BufferedOutputStream bos = null;
+        try {
+            bis = IoUtils.getBufferedInputStream(in);
+    
+            bos = IoUtils.getBufferedOutputStream( out );
+            
+            
+            while(true) {
+                
+                // if current rate exceeds the max rate, sleep for 50ms 
+                // and again check the current transfer rate
+                if(maxRate > 0) {
+                    
+                    // prevent "divide by zero" exception
+                    long interval = System.currentTimeMillis() - startTime;
+                    if(interval == 0) {
+                        interval = 1;
+                    }
+                    
+                    // check current rate
+                    long currRate = (transferredSize*1000L)/interval;
+                    if(currRate > maxRate) {
+                        try { Thread.sleep(50); } catch(InterruptedException ex) {break;}
+                        continue;
+                    }
+                }
+                
+                // read data
+                int count = bis.read(buff);
+    
+                if(count == -1) {
+                    break;
+                }
+                
+                // write data
+                // if ascii, replace \n by \r\n
+                if(isAscii) {
+                    for(int i=0; i<count; ++i) {
+                        byte b = buff[i];
+                        if(b == '\n') {
+                            bos.write('\r');
+                        }
+                        bos.write(b);
+                    }
+                }
+                else {
+                    bos.write(buff, 0, count);
+                }
+                
+                transferredSize += count;
+                
+                notifyObserver();
+            }
+        } finally {
+            if(bos != null) {
+                bos.flush();
+            }
+        }
+
+        return transferredSize;
+    }
+    
+    /**
+     * Notify connection manager observer.
+     */
+    protected void notifyObserver() {
+        session.updateLastAccessTime();
+        
+        // TODO this has been moved from AbstractConnection, do we need to keep it?
+        // serverContext.getConnectionManager().updateConnection(this);
+    }
+}
+    

Propchange: incubator/ftpserver/trunk/core/src/java/org/apache/ftpserver/FtpDataConnection.java
------------------------------------------------------------------------------
    svn:eol-style = native