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.common.file.passthrough;
22  
23  import goldengate.common.command.exception.CommandAbstractException;
24  import goldengate.common.command.exception.Reply450Exception;
25  import goldengate.common.command.exception.Reply502Exception;
26  import goldengate.common.command.exception.Reply530Exception;
27  import goldengate.common.command.exception.Reply550Exception;
28  import goldengate.common.exception.FileEndOfTransferException;
29  import goldengate.common.exception.FileTransferException;
30  import goldengate.common.exception.NoRestartException;
31  import goldengate.common.file.DataBlock;
32  import goldengate.common.file.DirInterface;
33  import goldengate.common.file.Restart;
34  import goldengate.common.file.SessionInterface;
35  import goldengate.common.logging.GgInternalLogger;
36  import goldengate.common.logging.GgInternalLoggerFactory;
37  
38  import java.io.File;
39  import java.io.IOException;
40  import java.nio.channels.FileChannel;
41  
42  import org.jboss.netty.buffer.ChannelBuffer;
43  
44  /**
45   * File implementation for Passthrough Based.
46   * It is just an empty shell since in pass through mode, no directories or files really exist.
47   *
48   * If one wants to implement special actions, he/she just has to extend this class and override
49   * the default empty implementation.
50   *
51   * @author Frederic Bregier
52   *
53   */
54  public abstract class PassthroughBasedFileImpl implements
55          goldengate.common.file.FileInterface {
56      /**
57       * Internal Logger
58       */
59      private static final GgInternalLogger logger = GgInternalLoggerFactory
60              .getLogger(PassthroughBasedFileImpl.class);
61  
62      /**
63       * SessionInterface
64       */
65      protected final SessionInterface session;
66  
67      /**
68       * DirInterface associated with this file at creation. It is not necessary
69       * the directory that owns this file.
70       */
71      private final PassthroughBasedDirImpl dir;
72  
73      /**
74       * {@link PassthroughBasedAuthImpl}
75       */
76      private final PassthroughBasedAuthImpl auth;
77  
78      /**
79       * Current file if any
80       */
81      protected String currentFile = null;
82  
83      /**
84       * Is this Document ready to be accessed
85       */
86      protected boolean isReady = false;
87  
88      /**
89       * Is this file in append mode
90       */
91      protected boolean isAppend = false;
92  
93      /**
94       * Passthrough object
95       */
96      protected PassthroughFile pfile = null;
97      /**
98       * Factory for PassthroughFile
99       */
100     public static PassthroughFileFactory factory = null;
101 
102     /**
103      * @param session
104      * @param dir
105      *            It is not necessary the directory that owns this file.
106      * @param path
107      * @param append
108      * @throws CommandAbstractException
109      * @throws PassthroughException
110      */
111     public PassthroughBasedFileImpl(SessionInterface session,
112             PassthroughBasedDirImpl dir, String path, boolean append)
113             throws CommandAbstractException {
114         this.session = session;
115         auth = (PassthroughBasedAuthImpl) session.getAuth();
116         this.dir = dir;
117         currentFile = path;
118         isReady = true;
119         isAppend = append;
120         File file = getFileFromPath(path);
121         try {
122             pfile = factory.create(this);
123         } catch (PassthroughException e1) {
124             throw new Reply450Exception(e1.getMessage());
125         }
126         if (append) {
127             try {
128                 setPosition(file.length());
129             } catch (IOException e) {
130                 logger.error("Error during position:", e);
131             }
132         } else {
133             try {
134                 setPosition(0);
135             } catch (IOException e) {
136             }
137         }
138     }
139 
140     public void clear() throws CommandAbstractException {
141         closeFile();
142         isReady = false;
143         currentFile = null;
144         isAppend = false;
145     }
146 
147     public void checkIdentify() throws Reply530Exception {
148         if (!getSession().getAuth().isIdentified()) {
149             throw new Reply530Exception("User not authentified");
150         }
151     }
152 
153     public SessionInterface getSession() {
154         return session;
155     }
156 
157     public DirInterface getDir() {
158         return dir;
159     }
160 
161     /**
162      * Get the File from this path, checking first its validity
163      *
164      * @param path
165      * @return the FileInterface
166      * @throws CommandAbstractException
167      */
168     protected File getFileFromPath(String path) throws CommandAbstractException {
169         String newdir = getDir().validatePath(path);
170         String truedir = auth.getAbsolutePath(newdir);
171         return new File(truedir);
172     }
173 
174     /**
175      * Get the relative path (without mount point)
176      *
177      * @param file
178      * @return the relative path
179      */
180     protected String getRelativePath(File file) {
181         return auth.getRelativePath(PassthroughBasedDirImpl.normalizePath(file
182                 .getAbsolutePath()));
183     }
184 
185     public boolean isDirectory() throws CommandAbstractException {
186         checkIdentify();
187         return pfile.isDirectory();
188     }
189 
190     public boolean isFile() throws CommandAbstractException {
191         checkIdentify();
192         return pfile.isFile();
193     }
194 
195     public String getFile() throws CommandAbstractException {
196         checkIdentify();
197         return currentFile;
198     }
199 
200     public boolean closeFile() throws CommandAbstractException {
201         try {
202             pfile.close();
203         } catch (PassthroughException e) {
204             throw new Reply450Exception(e.getMessage());
205         }
206         position = 0;
207         isReady = false;
208         // Do not clear the filename itself
209         return true;
210     }
211 
212     public boolean abortFile() throws CommandAbstractException {
213         if (isInWriting() &&
214                 ((PassthroughBasedFileParameterImpl) getSession()
215                         .getFileParameter()).deleteOnAbort) {
216             delete();
217         }
218         closeFile();
219         return true;
220     }
221 
222     public long length() throws CommandAbstractException {
223         checkIdentify();
224         if (!isReady) {
225             return -1;
226         }
227         if (!exists()) {
228             return -1;
229         }
230         return pfile.length();
231     }
232 
233     public boolean isInReading() throws CommandAbstractException {
234         if (!isReady) {
235             return false;
236         }
237         return pfile.isInReading();
238     }
239 
240     public boolean isInWriting() throws CommandAbstractException {
241         if (!isReady) {
242             return false;
243         }
244         return pfile.isInWriting();
245     }
246 
247     public boolean canRead() throws CommandAbstractException {
248         checkIdentify();
249         if (!isReady) {
250             return false;
251         }
252         return pfile.canRead();
253     }
254 
255     public boolean canWrite() throws CommandAbstractException {
256         checkIdentify();
257         if (!isReady) {
258             return false;
259         }
260         return pfile.canWrite();
261     }
262 
263     public boolean exists() throws CommandAbstractException {
264         checkIdentify();
265         if (!isReady) {
266             return false;
267         }
268         return pfile.exists();
269     }
270 
271     public boolean delete() throws CommandAbstractException {
272         checkIdentify();
273         if (!isReady) {
274             return false;
275         }
276         if (!exists()) {
277             return true;
278         }
279         closeFile();
280         try {
281             return pfile.delete();
282         } catch (PassthroughException e) {
283             throw new Reply550Exception(e.getMessage());
284         }
285     }
286 
287     public boolean renameTo(String path) throws CommandAbstractException {
288         checkIdentify();
289         if (!isReady) {
290             return false;
291         }
292         try {
293             return pfile.renameTo(path);
294         } catch (PassthroughException e) {
295             throw new Reply550Exception(e.getMessage());
296         }
297     }
298 
299     public DataBlock getMarker() throws CommandAbstractException {
300         throw new Reply502Exception("No marker implemented");
301     }
302 
303     public boolean restartMarker(Restart restart)
304             throws CommandAbstractException {
305         try {
306             long newposition = ((PassthroughBasedRestartImpl) restart)
307                     .getPosition();
308             try {
309                 setPosition(newposition);
310             } catch (IOException e) {
311                 throw new Reply502Exception("Cannot set the marker position");
312             }
313             return true;
314         } catch (NoRestartException e) {
315         }
316         return false;
317     }
318 
319     public boolean retrieve() throws CommandAbstractException {
320         checkIdentify();
321         if (isReady) {
322             restartMarker(getSession().getRestart());
323             return canRead();
324         }
325         return false;
326     }
327 
328     public boolean store() throws CommandAbstractException {
329         checkIdentify();
330         if (isReady) {
331             restartMarker(getSession().getRestart());
332             return canWrite();
333         }
334         return false;
335     }
336 
337     public DataBlock readDataBlock() throws FileTransferException,
338             FileEndOfTransferException {
339         if (isReady) {
340             DataBlock dataBlock = new DataBlock();
341             ChannelBuffer buffer = null;
342             buffer = getBlock(getSession().getBlockSize());
343             if (buffer != null) {
344                 dataBlock.setBlock(buffer);
345                 if (dataBlock.getByteCount() < getSession().getBlockSize()) {
346                     dataBlock.setEOF(true);
347                 }
348                 return dataBlock;
349             }
350         }
351         throw new FileTransferException("No file is ready");
352     }
353 
354     public void writeDataBlock(DataBlock dataBlock)
355             throws FileTransferException {
356         if (isReady) {
357             if (dataBlock.isEOF()) {
358                 writeBlockEnd(dataBlock.getBlock());
359                 return;
360             }
361             writeBlock(dataBlock.getBlock());
362             return;
363         }
364         throw new FileTransferException("No file is ready");
365     }
366 
367     /**
368      * Valid Position of this file
369      */
370     private long position = 0;
371 
372     /**
373      * Return the current position in the FileInterface. In write mode, it is
374      * the current file length.
375      *
376      * @return the position
377      */
378     public long getPosition() {
379         return position;
380     }
381 
382     /**
383      * Change the position in the file.
384      *
385      * @param position
386      *            the position to set
387      * @throws IOException
388      */
389     public void setPosition(long position) throws IOException {
390         try {
391             pfile.position(position);
392         } catch (PassthroughException e) {
393             throw new IOException(e);
394         }
395         this.position = position;
396     }
397     /**
398      * Try to flush written data if possible
399      */
400     public void flush() {
401         if (isReady) {
402             try {
403                 pfile.flush();
404             } catch (PassthroughException e) {
405             }
406         }
407     }
408     /**
409      * Write the current FileInterface with the given ChannelBuffer. The file is
410      * not limited to 2^32 bytes since this write operation is in add mode.
411      *
412      * In case of error, the current already written blocks are maintained and
413      * the position is not changed.
414      *
415      * @param buffer
416      *            added to the file
417      * @throws FileTransferException
418      */
419     private void writeBlock(ChannelBuffer buffer) throws FileTransferException {
420         if (!isReady) {
421             throw new FileTransferException("No file is ready");
422         }
423         // An empty buffer is allowed
424         if (buffer == null) {
425             return;// could do FileEndOfTransfer ?
426         }
427         int bufferSize = buffer.readableBytes();
428         int size;
429         try {
430             size = pfile.write(buffer);
431         } catch (PassthroughException e) {
432             throw new FileTransferException("Cannot write to file");
433         }
434         boolean result = size == bufferSize;
435         if (!result) {
436             try {
437                 pfile.close();
438             } catch (PassthroughException e) {
439             }
440             // NO this.realFile.delete(); NO DELETE SINCE BY BLOCK IT CAN BE
441             // REDO
442             throw new FileTransferException("Internal error, file is not ready");
443         }
444         position += size;
445     }
446 
447     /**
448      * End the Write of the current FileInterface with the given ChannelBuffer.
449      * The file is not limited to 2^32 bytes since this write operation is in
450      * add mode.
451      *
452      * @param buffer
453      *            added to the file
454      * @throws FileTransferException
455      */
456     private void writeBlockEnd(ChannelBuffer buffer)
457             throws FileTransferException {
458         writeBlock(buffer);
459         try {
460             closeFile();
461         } catch (CommandAbstractException e) {
462         }
463     }
464 
465     /**
466      * Get the current block ChannelBuffer of the current FileInterface. There
467      * is therefore no limitation of the file size to 2^32 bytes.
468      *
469      * The returned block is limited to sizeblock. If the returned block is less
470      * than sizeblock length, it is the last block to read.
471      *
472      * @param sizeblock
473      *            is the limit size for the block array
474      * @return the resulting block ChannelBuffer (even empty)
475      * @throws FileTransferException
476      * @throws FileEndOfTransferException
477      */
478     private ChannelBuffer getBlock(int sizeblock) throws FileTransferException,
479             FileEndOfTransferException {
480         if (!isReady) {
481             throw new FileTransferException("No file is ready");
482         }
483         ChannelBuffer buffer;
484         try {
485             buffer = pfile.read(sizeblock);
486         } catch (PassthroughException e) {
487             throw new FileEndOfTransferException("Cannot read the file");
488         }
489         int sizeout = buffer.readableBytes();
490         if (sizeout < sizeblock) {// last block
491             try {
492                 pfile.close();
493             } catch (PassthroughException e) {
494             }
495             isReady = false;
496         }
497         if (sizeout <= 0) {
498             throw new FileEndOfTransferException("End of file");
499         }
500         position += sizeout;
501         return buffer;
502     }
503 
504     /**
505      * Write the FileInterface to the fileChannelOut, thus bypassing the
506      * limitation of the file size to 2^32 bytes.
507      *
508      * This call closes the fileChannelOut with fileChannelOut.close() if the
509      * operation is in success.
510      *
511      * @param fileChannelOut
512      * @return True if OK, False in error.
513      */
514     protected boolean get(FileChannel fileChannelOut) {
515         if (!isReady) {
516             return false;
517         }
518         long size = pfile.length();
519         long transfert;
520         try {
521             transfert = pfile.transferTo(fileChannelOut);
522         } catch (PassthroughException e) {
523             return false;
524         }
525         if (transfert == size) {
526             position += size;
527         }
528         return transfert == size;
529     }
530 }