View Javadoc

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