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.filesystembased;
22  
23  import goldengate.common.command.exception.CommandAbstractException;
24  import goldengate.common.command.exception.Reply550Exception;
25  import goldengate.common.command.exception.Reply553Exception;
26  import goldengate.common.digest.FilesystemBasedDigest;
27  import goldengate.common.file.AbstractDir;
28  import goldengate.common.file.FileInterface;
29  import goldengate.common.file.OptsMLSxInterface;
30  import goldengate.common.file.SessionInterface;
31  import goldengate.common.file.filesystembased.specific.FilesystemBasedCommonsIo;
32  import goldengate.common.file.filesystembased.specific.FilesystemBasedDirJdkAbstract;
33  import goldengate.common.logging.GgInternalLogger;
34  import goldengate.common.logging.GgInternalLoggerFactory;
35  
36  import java.io.File;
37  import java.io.FileFilter;
38  import java.io.FileInputStream;
39  import java.io.FileNotFoundException;
40  import java.io.IOException;
41  import java.text.DateFormat;
42  import java.text.SimpleDateFormat;
43  import java.util.ArrayList;
44  import java.util.Calendar;
45  import java.util.Date;
46  import java.util.List;
47  import java.util.Locale;
48  import java.util.zip.CRC32;
49  import java.util.zip.CheckedInputStream;
50  
51  /**
52   * Directory implementation for Filesystem Based
53   *
54   * @author Frederic Bregier
55   *
56   */
57  public abstract class FilesystemBasedDirImpl extends AbstractDir {
58      /**
59       * Internal Logger
60       */
61      private static final GgInternalLogger logger = GgInternalLoggerFactory
62              .getLogger(FilesystemBasedDirImpl.class);
63  
64      /**
65       * Class that handles specifity of one Jdk or another
66       */
67      protected static FilesystemBasedDirJdkAbstract filesystemBasedFtpDirJdk = null;
68      /**
69       * Init the dependant object according to internals of JDK
70       *
71       * @param filesystemBasedFtpDirJdkChoice
72       */
73      public static void initJdkDependent(
74              FilesystemBasedDirJdkAbstract filesystemBasedFtpDirJdkChoice) {
75          filesystemBasedFtpDirJdk = filesystemBasedFtpDirJdkChoice;
76          initWindowsSupport();
77      }
78  
79      /**
80       * @param session
81       * @param optsMLSx
82       */
83      public FilesystemBasedDirImpl(SessionInterface session,
84              OptsMLSxInterface optsMLSx) {
85          this.session = session;
86          this.optsMLSx = optsMLSx;
87          this.optsMLSx.setOptsModify((byte) 1);
88          this.optsMLSx.setOptsPerm((byte) 1);
89          this.optsMLSx.setOptsSize((byte) 1);
90          this.optsMLSx.setOptsType((byte) 1);
91      }
92  
93  
94      /**
95       * Finds all files matching a wildcard expression (based on '?', '~' or
96       * '*').
97       *
98       * @param pathWithWildcard
99       *            The wildcard expression with a business path.
100      * @return List of String as relative paths matching the wildcard
101      *         expression. Those files are tested as valid from business point
102      *         of view. If Wildcard support is not active, if the path contains
103      *         any wildcards, it will throw an error.
104      * @throws CommandAbstractException
105      */
106     protected List<String> wildcardFiles(String pathWithWildcard)
107             throws CommandAbstractException {
108         List<String> resultPaths = new ArrayList<String>();
109         // First check if pathWithWildcard contains wildcards
110         if (!(pathWithWildcard.contains("*") || pathWithWildcard.contains("?") || pathWithWildcard
111                 .contains("~"))) {
112             // No so simply return the list containing this path after
113             // validating it
114             if (getSession().getAuth().isBusinessPathValid(pathWithWildcard)) {
115                 resultPaths.add(pathWithWildcard);
116             }
117             return resultPaths;
118         }
119         // Do we support Wildcard path
120         if (!FilesystemBasedDirJdkAbstract.ueApacheCommonsIo) {
121             throw new Reply553Exception("Wildcards in pathname is not allowed");
122         }
123         File wildcardFile;
124         File rootFile;
125         if (isAbsoluteWindows(pathWithWildcard)) {
126             wildcardFile = new File(pathWithWildcard);
127             rootFile = getCorrespondingRoot(wildcardFile);
128         } else {
129             rootFile = new File(((FilesystemBasedAuthImpl) getSession()
130                     .getAuth()).getBaseDirectory());
131             wildcardFile = new File(rootFile, pathWithWildcard);
132         }
133         // Split wildcard path into subdirectories.
134         List<String> subdirs = new ArrayList<String>();
135         while (wildcardFile != null) {
136             File parent = wildcardFile.getParentFile();
137             if (parent == null) {
138                 subdirs.add(0, wildcardFile.getPath());
139                 break;
140             }
141             subdirs.add(0, wildcardFile.getName());
142             if (parent.equals(rootFile)) {
143                 // End of wildcard path
144                 subdirs.add(0, parent.getPath());
145                 break;
146             }
147             wildcardFile = parent;
148         }
149         List<File> basedPaths = new ArrayList<File>();
150         // First set root
151         basedPaths.add(new File(subdirs.get(0)));
152         int i = 1;
153         // For each wilcard subdirectory
154         while (i < subdirs.size()) {
155             // Set current filter
156             FileFilter fileFilter = FilesystemBasedCommonsIo
157                     .getWildcardFileFilter(subdirs.get(i));
158             List<File> newBasedPaths = new ArrayList<File>();
159             // Look for matches in all the current search paths
160             for (File dir: basedPaths) {
161                 if (dir.isDirectory()) {
162                     for (File match: dir.listFiles(fileFilter)) {
163                         newBasedPaths.add(match);
164                     }
165                 }
166             }
167             // base Search Path changes now
168             basedPaths = newBasedPaths;
169             i ++;
170         }
171         // Valid each file first
172         for (File file: basedPaths) {
173             String relativePath = ((FilesystemBasedAuthImpl) getSession()
174                     .getAuth()).getRelativePath(normalizePath(file
175                     .getAbsolutePath()));
176             String newpath = this.validatePath(relativePath);
177             resultPaths.add(newpath);
178         }
179         return resultPaths;
180     }
181 
182     /**
183      * Get the FileInterface from this path, checking first its validity
184      *
185      * @param path
186      * @return the FileInterface
187      * @throws CommandAbstractException
188      */
189     protected File getFileFromPath(String path) throws CommandAbstractException {
190         String newdir = validatePath(path);
191         if (isAbsoluteWindows(newdir)) {
192             return new File(newdir);
193         }
194         String truedir = ((FilesystemBasedAuthImpl) getSession().getAuth())
195                 .getAbsolutePath(newdir);
196         return new File(truedir);
197     }
198 
199     /**
200      * Get the true file from the path
201      *
202      * @param path
203      * @return the true File from the path
204      * @throws CommandAbstractException
205      */
206     protected File getTrueFile(String path) throws CommandAbstractException {
207         checkIdentify();
208         String newpath = consolidatePath(path);
209         List<String> paths = wildcardFiles(normalizePath(newpath));
210         if (paths.size() != 1) {
211             throw new Reply550Exception("File not found: " + paths.size() +
212                     " founds");
213         }
214         String extDir = paths.get(0);
215         extDir = this.validatePath(extDir);
216         File file = getFileFromPath(extDir);
217         if (!file.isFile()) {
218             throw new Reply550Exception("Path is not a file: " + path);
219         }
220         return file;
221     }
222 
223     /**
224      * Get the relative path (without mount point)
225      *
226      * @param file
227      * @return the relative path
228      */
229     protected String getRelativePath(File file) {
230         return ((FilesystemBasedAuthImpl) getSession().getAuth())
231                 .getRelativePath(normalizePath(file.getAbsolutePath()));
232     }
233 
234     public boolean changeDirectory(String path) throws CommandAbstractException {
235         checkIdentify();
236         String newpath = consolidatePath(path);
237         List<String> paths = wildcardFiles(newpath);
238         if (paths.size() != 1) {
239             logger.warn("CD error: {}", newpath);
240             throw new Reply550Exception("Directory not found: " + paths.size() +
241                     " founds");
242         }
243         String extDir = paths.get(0);
244         extDir = this.validatePath(extDir);
245         if (isDirectory(extDir)) {
246             currentDir = extDir;
247             return true;
248         }
249         throw new Reply550Exception("Directory not found: "+extDir);
250     }
251 
252     public boolean changeDirectoryNotChecked(String path) throws CommandAbstractException {
253         checkIdentify();
254         String newpath = consolidatePath(path);
255         List<String> paths = wildcardFiles(newpath);
256         if (paths.size() != 1) {
257             logger.warn("CD error: {}", newpath);
258             throw new Reply550Exception("Directory not found: " + paths.size() +
259                     " founds");
260         }
261         String extDir = paths.get(0);
262         extDir = this.validatePath(extDir);
263         return true;
264     }
265 
266     public String mkdir(String directory) throws CommandAbstractException {
267         checkIdentify();
268         String newdirectory = consolidatePath(directory);
269         File dir = new File(newdirectory);
270         String parent = dir.getParentFile().getPath();
271         List<String> paths = wildcardFiles(normalizePath(parent));
272         if (paths.size() != 1) {
273             throw new Reply550Exception("Base Directory not found: " +
274                     paths.size() + " founds");
275         }
276         String newDir = paths.get(0) + SEPARATOR + dir.getName();
277         newDir = this.validatePath(newDir);
278         File newdir = getFileFromPath(newDir);
279         if (newdir.mkdir()) {
280             return newDir;
281         }
282         throw new Reply550Exception("Cannot create directory " + newDir);
283     }
284 
285     public String rmdir(String directory) throws CommandAbstractException {
286         checkIdentify();
287         String newdirectory = consolidatePath(directory);
288         List<String> paths = wildcardFiles(normalizePath(newdirectory));
289         if (paths.size() != 1) {
290             throw new Reply550Exception("Directory not found: " + paths.size() +
291                     " founds");
292         }
293         String extDir = paths.get(0);
294         extDir = this.validatePath(extDir);
295         File dir = getFileFromPath(extDir);
296         if (dir.delete()) {
297             return extDir;
298         }
299         throw new Reply550Exception("Cannot delete directory " + extDir);
300     }
301 
302     public boolean isDirectory(String path) throws CommandAbstractException {
303         checkIdentify();
304         File dir = getFileFromPath(path);
305         return dir.isDirectory();
306     }
307 
308     public boolean isFile(String path) throws CommandAbstractException {
309         checkIdentify();
310         return getFileFromPath(path).isFile();
311     }
312 
313     public String getModificationTime(String path)
314             throws CommandAbstractException {
315         checkIdentify();
316         File file = getFileFromPath(path);
317         if (file.exists()) {
318             return getModificationTime(file);
319         }
320         throw new Reply550Exception("\"" + path + "\" does not exist");
321     }
322 
323     /**
324      * Return the Modification time for the File
325      *
326      * @param file
327      * @return the Modification time as a String YYYYMMDDHHMMSS.sss
328      */
329     protected String getModificationTime(File file) {
330         long mstime = file.lastModified();
331         Calendar calendar = Calendar.getInstance();
332         calendar.setTimeInMillis(mstime);
333         int year = calendar.get(Calendar.YEAR);
334         int month = calendar.get(Calendar.MONTH) + 1;
335         int day = calendar.get(Calendar.DAY_OF_MONTH);
336         int hour = calendar.get(Calendar.HOUR_OF_DAY);
337         int minute = calendar.get(Calendar.MINUTE);
338         int second = calendar.get(Calendar.SECOND);
339         int ms = calendar.get(Calendar.MILLISECOND);
340         StringBuilder sb = new StringBuilder(18);
341         sb.append(year);
342         if (month < 10) {
343             sb.append(0);
344         }
345         sb.append(month);
346         if (day < 10) {
347             sb.append(0);
348         }
349         sb.append(day);
350         if (hour < 10) {
351             sb.append(0);
352         }
353         sb.append(hour);
354         if (minute < 10) {
355             sb.append(0);
356         }
357         sb.append(minute);
358         if (second < 10) {
359             sb.append(0);
360         }
361         sb.append(second);
362         sb.append('.');
363         if (ms < 10) {
364             sb.append(0);
365         }
366         if (ms < 100) {
367             sb.append(0);
368         }
369         sb.append(ms);
370         return sb.toString();
371     }
372 
373     public List<String> list(String path) throws CommandAbstractException {
374         checkIdentify();
375         // First get all base directories
376         String newpath = path;
377         if (newpath == null || newpath.length() == 0) {
378             newpath = currentDir;
379         }
380         if (newpath.startsWith("-a") || newpath.startsWith("-A")) {
381             String[] args = newpath.split(" ");
382             if (args.length > 1) {
383                 newpath = args[1];
384             } else {
385                 newpath = currentDir;
386             }
387         }
388         newpath = consolidatePath(newpath);
389         List<String> paths = wildcardFiles(newpath);
390         if (paths.isEmpty()) {
391             throw new Reply550Exception("No files found");
392         }
393         // Now if they are directories, list inside them
394         List<String> newPaths = new ArrayList<String>();
395         for (String file: paths) {
396             File dir = getFileFromPath(file);
397             if (dir.exists()) {
398                 if (dir.isDirectory()) {
399                     String[] files = dir.list();
400                     for (String finalFile: files) {
401                         String relativePath = ((FilesystemBasedAuthImpl) getSession()
402                                 .getAuth()).getRelativePath(finalFile);
403                         newPaths.add(relativePath);
404                     }
405                 } else {
406                     newPaths.add(file);
407                 }
408             }
409         }
410         return newPaths;
411     }
412 
413     public List<String> listFull(String path, boolean lsFormat)
414             throws CommandAbstractException {
415         checkIdentify();
416         boolean listAllFiles = false;
417         String newpath = path;
418         if (newpath == null || newpath.length() == 0) {
419             newpath = currentDir;
420         }
421         if (newpath.startsWith("-a") || newpath.startsWith("-A")) {
422             String[] args = newpath.split(" ");
423             if (args.length > 1) {
424                 newpath = args[1];
425             } else {
426                 newpath = currentDir;
427             }
428             listAllFiles = true;
429         }
430         newpath = consolidatePath(newpath);
431         // First get all base directories
432         List<String> paths = wildcardFiles(newpath);
433         if (paths.isEmpty()) {
434             throw new Reply550Exception("No files found");
435         }
436         // Now if they are directories, list inside them
437         List<String> newPaths = new ArrayList<String>();
438         for (String file: paths) {
439             File dir = getFileFromPath(file);
440             if (dir.exists()) {
441                 if (dir.isDirectory()) {
442                     File[] files = dir.listFiles();
443                     for (File finalFile: files) {
444                         if (lsFormat) {
445                             newPaths.add(lsInfo(finalFile));
446                         } else {
447                             newPaths.add(mlsxInfo(finalFile));
448                         }
449                     }
450                 } else {
451                     if (lsFormat) {
452                         newPaths.add(lsInfo(dir));
453                     } else {
454                         newPaths.add(mlsxInfo(dir));
455                     }
456                 }
457             }
458         }
459         if (listAllFiles) {
460             File dir = new File(getFileFromPath(newpath), SEPARATOR + "..");
461             if (lsFormat) {
462                 newPaths.add(lsInfo(dir));
463             } else {
464                 newPaths.add(mlsxInfo(dir));
465             }
466         }
467         return newPaths;
468     }
469 
470     public String fileFull(String path, boolean lsFormat)
471             throws CommandAbstractException {
472         checkIdentify();
473         String newpath = consolidatePath(path);
474         List<String> paths = wildcardFiles(normalizePath(newpath));
475         if (paths.size() != 1) {
476             throw new Reply550Exception("No files found " + paths.size() +
477                     " founds");
478         }
479         File file = getFileFromPath(paths.get(0));
480         if (file.exists()) {
481             if (lsFormat) {
482                 return "Listing of \"" + paths.get(0) + "\"\n" + lsInfo(file) +
483                         "\nEnd of listing";
484             }
485             return "Listing of \"" + paths.get(0) + "\"\n" + mlsxInfo(file) +
486                     "\nEnd of listing";
487         }
488         return "No file with name \"" + path + "\"";
489     }
490 
491     /**
492      * Decide if Full time or partial time as in 'ls' command
493      *
494      * @return True if Full Time, False is Default (as in 'ls' command)
495      */
496     protected boolean isFullTime() {
497         // FIXME should be it the default ?
498         return false;
499     }
500 
501     /**
502      *
503      * @param file
504      * @return the ls format information
505      */
506     protected String lsInfo(File file) {
507         // Unix FileInterface type,permissions,hard
508         // link(?),owner(?),group(?),size,date
509         // and filename
510         StringBuilder builder = new StringBuilder();
511         builder.append((file.isDirectory()? 'd' : '-'));
512         builder.append((file.canRead()? 'r' : '-'));
513         builder.append((file.canWrite()? 'w' : '-'));
514         if (filesystemBasedFtpDirJdk != null)
515             builder.append(filesystemBasedFtpDirJdk.canExecute(file)? 'x' : '-');
516         else
517             builder.append('-');
518         // Group and others not supported
519         builder.append("---");
520         builder.append("---");
521         builder.append(' ');
522         builder.append("1 ");// hard link ?
523         builder.append("anybody\t");// owner ?
524         builder.append("anygroup\t");// group ?
525         builder.append(file.length());// size
526         builder.append('\t');
527         long lastmod = file.lastModified();
528         String fmt = null;
529         // It seems Full Time is not recognized by some FTP client
530         /*
531          * if(isFullTime()) { fmt = "EEE MMM dd HH:mm:ss yyyy"; } else {
532          */
533         long currentTime = System.currentTimeMillis();
534         if (currentTime > lastmod + 6L * 30L * 24L * 60L * 60L * 1000L // Old.
535                 || currentTime < lastmod - 60L * 60L * 1000L) { // In the
536             // future.
537             // The file is fairly old or in the future.
538             // POSIX says the cutoff is 6 months old;
539             // approximate this by 6*30 days.
540             // Allow a 1 hour slop factor for what is considered "the future",
541             // to allow for NFS server/client clock disagreement.
542             // Show the year instead of the time of day.
543             fmt = "MMM dd  yyyy";
544         } else {
545             fmt = "MMM dd HH:mm";
546         }
547         /* } */
548         SimpleDateFormat dateFormat = (SimpleDateFormat) DateFormat
549                 .getDateTimeInstance(DateFormat.LONG, DateFormat.LONG,
550                         Locale.ENGLISH);
551         dateFormat.applyPattern(fmt);
552         builder.append(dateFormat.format(new Date(lastmod)));// date
553         builder.append('\t');
554         builder.append(file.getName());
555         return builder.toString();
556     }
557 
558     /**
559      *
560      * @param file
561      * @return the MLSx information: ' Fact=facts;...; filename'
562      */
563     protected String mlsxInfo(File file) {
564         // don't have create, unique, lang, media-type, charset
565         StringBuilder builder = new StringBuilder(" ");
566         if (getOptsMLSx().getOptsSize() == 1) {
567             builder.append("Size=");
568             builder.append(file.length());
569             builder.append(';');
570         }
571         if (getOptsMLSx().getOptsModify() == 1) {
572             builder.append("Modify=");
573             builder.append(this.getModificationTime(file));
574             builder.append(';');
575         }
576         if (getOptsMLSx().getOptsType() == 1) {
577             builder.append("Type=");
578             try {
579                 if (getFileFromPath(currentDir).equals(file)) {
580                     builder.append("cdir");
581                 } else {
582                     if (file.isDirectory()) {
583                         builder.append("dir");
584                     } else {
585                         builder.append("file");
586                     }
587                 }
588             } catch (CommandAbstractException e) {
589                 if (file.isDirectory()) {
590                     builder.append("dir");
591                 } else {
592                     builder.append("file");
593                 }
594             }
595             builder.append(';');
596         }
597         if (getOptsMLSx().getOptsPerm() == 1) {
598             builder.append("Perm=");
599             if (file.isFile()) {
600                 if (file.canWrite()) {
601                     builder.append('a');
602                     builder.append('d');
603                     builder.append('f');
604                     builder.append('w');
605                 }
606                 if (file.canRead()) {
607                     builder.append('r');
608                 }
609             } else {
610                 // Directory
611                 if (file.canWrite()) {
612                     builder.append('c');
613                     try {
614                         if (this.validatePath(file) != null) {
615                             builder.append('d');
616                             builder.append('m');
617                             builder.append('p');
618                         }
619                     } catch (CommandAbstractException e) {
620                     }
621                 }
622                 if (file.canRead()) {
623                     builder.append('l');
624                     builder.append('e');
625                 }
626             }
627             builder.append(';');
628         }
629 
630         builder.append(' ');
631         builder.append(file.getName());
632         return builder.toString();
633     }
634 
635     public long getFreeSpace() throws CommandAbstractException {
636         checkIdentify();
637         File directory = getFileFromPath(currentDir);
638         if (filesystemBasedFtpDirJdk != null)
639             return filesystemBasedFtpDirJdk.getFreeSpace(directory);
640         else
641             return Integer.MAX_VALUE;
642     }
643 
644     public FileInterface setUniqueFile()
645             throws CommandAbstractException {
646         checkIdentify();
647         File file = null;
648         try {
649             file = File.createTempFile(getSession().getAuth().getUser(),
650                     this.session.getUniqueExtension(), getFileFromPath(currentDir));
651         } catch (IOException e) {
652             throw new Reply550Exception("Cannot create unique file");
653         }
654         String currentFile = getRelativePath(file);
655         return newFile(normalizePath(currentFile), false);
656     }
657 
658     public boolean canRead() throws CommandAbstractException {
659         checkIdentify();
660         return getFileFromPath(currentDir).canRead();
661     }
662 
663     public boolean canWrite() throws CommandAbstractException {
664         checkIdentify();
665         File file = getFileFromPath(currentDir);
666         return file.canWrite();
667     }
668 
669     public boolean exists() throws CommandAbstractException {
670         checkIdentify();
671         return getFileFromPath(currentDir).exists();
672     }
673 
674     public long getCRC(String path) throws CommandAbstractException {
675         File file = getTrueFile(path);
676         try {
677             CheckedInputStream cis = null;
678             try {
679                 // Computer CRC32 checksum
680                 cis = new CheckedInputStream(new FileInputStream(file),
681                         new CRC32());
682             } catch (FileNotFoundException e) {
683                 throw new Reply550Exception("File not found: " + path);
684             }
685             byte[] buf = new byte[session.getBlockSize()];
686             while (cis.read(buf) >= 0) {
687             }
688             return cis.getChecksum().getValue();
689         } catch (IOException e) {
690             throw new Reply550Exception("Error while reading file: " + path);
691         }
692     }
693 
694     public byte[] getMD5(String path) throws CommandAbstractException {
695         File file = getTrueFile(path);
696         try {
697             if (FilesystemBasedFileParameterImpl.useNio) {
698                 return FilesystemBasedDigest.getHashMd5Nio(file);
699             }
700             return FilesystemBasedDigest.getHashMd5(file);
701         } catch (IOException e1) {
702             throw new Reply550Exception("Error while reading file: " + path);
703         }
704     }
705 
706     public byte[] getSHA1(String path) throws CommandAbstractException {
707         File file = getTrueFile(path);
708         try {
709             if (FilesystemBasedFileParameterImpl.useNio) {
710                 return FilesystemBasedDigest.getHashSha1Nio(file);
711             }
712             return FilesystemBasedDigest.getHashSha1(file);
713         } catch (IOException e1) {
714             throw new Reply550Exception("Error while reading file: " + path);
715         }
716     }
717 
718 }