View Javadoc

1   /**
2    * This file is part of Waarp Project.
3    * 
4    * Copyright 2009, Frederic Bregier, and individual contributors by the @author tags. See the
5    * COPYRIGHT.txt in the distribution for a full listing of individual contributors.
6    * 
7    * All Waarp Project is free software: you can redistribute it and/or modify it under the terms of
8    * the GNU General Public License as published by the Free Software Foundation, either version 3 of
9    * the License, or (at your option) any later version.
10   * 
11   * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
12   * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
13   * Public License for more details.
14   * 
15   * You should have received a copy of the GNU General Public License along with Waarp . If not, see
16   * <http://www.gnu.org/licenses/>.
17   */
18  package org.waarp.ftp.filesystembased;
19  
20  import java.util.concurrent.locks.ReentrantLock;
21  
22  import io.netty.channel.Channel;
23  import io.netty.channel.ChannelFuture;
24  
25  import org.waarp.common.command.exception.CommandAbstractException;
26  import org.waarp.common.exception.FileEndOfTransferException;
27  import org.waarp.common.exception.FileTransferException;
28  import org.waarp.common.file.DataBlock;
29  import org.waarp.common.file.filesystembased.FilesystemBasedFileImpl;
30  import org.waarp.common.logging.WaarpLogger;
31  import org.waarp.common.logging.WaarpLoggerFactory;
32  import org.waarp.ftp.core.exception.FtpNoConnectionException;
33  import org.waarp.ftp.core.file.FtpFile;
34  import org.waarp.ftp.core.session.FtpSession;
35  
36  /**
37   * Filesystem implementation of a FtpFile
38   * 
39   * @author Frederic Bregier
40   * 
41   */
42  public abstract class FilesystemBasedFtpFile extends FilesystemBasedFileImpl implements FtpFile {
43      /**
44       * Internal Logger
45       */
46      private static final WaarpLogger logger = WaarpLoggerFactory
47              .getLogger(FilesystemBasedFtpFile.class);
48  
49      /**
50       * Retrieve lock to ensure only one call at a time for one file
51       */
52      private final ReentrantLock retrieveLock = new ReentrantLock();
53  
54      /**
55       * @param session
56       * @param dir
57       *            It is not necessary the directory that owns this file.
58       * @param path
59       * @param append
60       * @throws CommandAbstractException
61       */
62      public FilesystemBasedFtpFile(FtpSession session,
63              FilesystemBasedFtpDir dir, String path, boolean append)
64              throws CommandAbstractException {
65          super(session, dir, path, append);
66      }
67  
68      @Override
69      public long length() throws CommandAbstractException {
70          long length = super.length();
71          if (((FtpSession) getSession()).getDataConn()
72                  .isFileStreamBlockAsciiImage()) {
73              long block = (long) Math.ceil((double) length /
74                      (double) getSession().getBlockSize());
75              length += (block + 3) * 3;
76          }
77          return length;
78      }
79  
80      /**
81       * Launch retrieve operation (internal method, should not be called directly)
82       * 
83       */
84      public void trueRetrieve() {
85          retrieveLock.lock();
86          try {
87              if (!isReady) {
88                  return;
89              }
90              // First check if ready to run from Control
91              try {
92                  ((FtpSession) session).getDataConn().getFtpTransferControl()
93                          .waitForDataNetworkHandlerReady();
94              } catch (InterruptedException e) {
95                  // bad thing
96                  logger.warn("DataNetworkHandler was not ready", e);
97                  return;
98              }
99              Channel channel = null;
100             try {
101                 channel = ((FtpSession) session).getDataConn().getCurrentDataChannel();
102             } catch (FtpNoConnectionException e) {
103                 if (this.isInReading()) {
104                     logger.error("Should not be", e);
105                     ((FtpSession) session).getDataConn().getFtpTransferControl()
106                             .setTransferAbortedFromInternal(true);
107                 }
108                 logger.debug("Possible call while channel was on going to be closed once transfer was done", e);
109                 closeFile();
110                 ((FtpSession) session).getDataConn().getFtpTransferControl()
111                         .setPreEndOfTransfer();
112                 return;
113             }
114             DataBlock block = null;
115             try {
116                 block = readDataBlock();
117             } catch (FileEndOfTransferException e) {
118                 // Last block (in fact, previous block was the last one,
119                 // but it could be aligned with the block size so not
120                 // detected)
121                 closeFile();
122                 ((FtpSession) session).getDataConn().getFtpTransferControl()
123                         .setPreEndOfTransfer();
124                 return;
125             }
126             if (block == null) {
127                 // Last block (in fact, previous block was the last one,
128                 // but it could be aligned with the block size so not
129                 // detected)
130                 closeFile();
131                 ((FtpSession) session).getDataConn().getFtpTransferControl()
132                         .setPreEndOfTransfer();
133                 return;
134             }
135             // While not last block
136             ChannelFuture future = null;
137             while (block != null && !block.isEOF()) {
138                 future = channel.writeAndFlush(block);
139                 try {
140                     future.await();
141                 } catch (InterruptedException e) {
142                 }
143                 if (!future.isSuccess()) {
144                     closeFile();
145                     throw new FileTransferException("File transfer in error");
146                 }
147                 try {
148                     block = readDataBlock();
149                 } catch (FileEndOfTransferException e) {
150                     closeFile();
151                     // Wait for last write
152                     if (future.isSuccess()) {
153                         ((FtpSession) session).getDataConn()
154                                 .getFtpTransferControl().setPreEndOfTransfer();
155                     } else {
156                         throw new FileTransferException("File transfer in error");
157                     }
158                     return;
159                 }
160             }
161             // Last block
162             closeFile();
163             if (block != null) {
164                 logger.debug("Write " + block.getByteCount());
165                 future = channel.writeAndFlush(block);
166             }
167             // Wait for last write
168             if (future != null) {
169                 try {
170                     future.await();
171                 } catch (InterruptedException e) {
172                 }
173                 if (future.isSuccess()) {
174                     ((FtpSession) session).getDataConn().getFtpTransferControl()
175                             .setPreEndOfTransfer();
176                 } else {
177                     throw new FileTransferException("Write is not successful");
178                 }
179             }
180         } catch (FileTransferException e) {
181             // An error occurs!
182             ((FtpSession) session).getDataConn().getFtpTransferControl()
183                     .setTransferAbortedFromInternal(true);
184         } catch (CommandAbstractException e) {
185             logger.error("Should not be", e);
186             ((FtpSession) session).getDataConn().getFtpTransferControl()
187                     .setTransferAbortedFromInternal(true);
188         } finally {
189             retrieveLock.unlock();
190         }
191     }
192 }