You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2019/07/05 00:54:51 UTC

[commons-vfs] branch master updated: [VFS-721] Add support for symbolic links for the local file system and add FileObject#isSymbolicLink().

This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-vfs.git


The following commit(s) were added to refs/heads/master by this push:
     new 2174c55  [VFS-721] Add support for symbolic links for the local file system and add FileObject#isSymbolicLink().
2174c55 is described below

commit 2174c55e87292b6d6ee0dabe0334713ed1668c43
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Thu Jul 4 20:54:48 2019 -0400

    [VFS-721] Add support for symbolic links for the local file system and
    add FileObject#isSymbolicLink().
---
 .../java/org/apache/commons/vfs2/FileObject.java   |  11 ++
 .../org/apache/commons/vfs2/Resources.properties   |   1 +
 .../vfs2/filter/SymbolicLinkFileFilter.java        |  89 ++++++++++++++
 .../commons/vfs2/provider/AbstractFileObject.java  |  31 +++++
 .../commons/vfs2/provider/local/LocalFile.java     |  10 ++
 .../apache/commons/vfs2/filter/BaseFilterTest.java |   6 +
 .../vfs2/filter/SymbolicLinkFileFilterTest.java    | 136 +++++++++++++++++++++
 .../commons/vfs2/test/ProviderReadTests.java       |   8 ++
 .../commons/vfs2/test/ProviderWriteTests.java      |   1 +
 src/changes/changes.xml                            |   3 +
 10 files changed, 296 insertions(+)

diff --git a/commons-vfs2/src/main/java/org/apache/commons/vfs2/FileObject.java b/commons-vfs2/src/main/java/org/apache/commons/vfs2/FileObject.java
index a6480b2..4ebe734 100644
--- a/commons-vfs2/src/main/java/org/apache/commons/vfs2/FileObject.java
+++ b/commons-vfs2/src/main/java/org/apache/commons/vfs2/FileObject.java
@@ -359,6 +359,17 @@ public interface FileObject extends Comparable<FileObject>, Iterable<FileObject>
     boolean isReadable() throws FileSystemException;
 
     /**
+     * Determines if this file is a symbolic link.
+     *
+     * @return {@code true} if this file is a symbolic link, {@code false} if not.
+     * @throws FileSystemException On error determining if this file exists.
+     * @since 2.4
+     */
+    default boolean isSymbolicLink() throws FileSystemException {
+        return false;
+    }
+
+    /**
      * Determines if this file can be written to.
      *
      * @return {@code true} if this file is writeable, {@code false} if not.
diff --git a/commons-vfs2/src/main/java/org/apache/commons/vfs2/Resources.properties b/commons-vfs2/src/main/java/org/apache/commons/vfs2/Resources.properties
index e5058b5..d9184d4 100644
--- a/commons-vfs2/src/main/java/org/apache/commons/vfs2/Resources.properties
+++ b/commons-vfs2/src/main/java/org/apache/commons/vfs2/Resources.properties
@@ -57,6 +57,7 @@ vfs.provider/check-is-executable.error=Could not determine if file "{0}" is exec
 vfs.provider/check-is-hidden.error=Could not determine if file "{0}" is hidden.
 vfs.provider/check-is-writeable.error=Could not determine if file "{0}" is writeable.
 vfs.provider/check-is-readable.error=Could not determine if file "{0}" is readable.
+vfs.provider/check-is-symbolic-link.error=Could not determine if file "{0}" is a symbolic link.
 vfs.provider/set-executable.error=Could not set the executable flag of file "{0}".
 vfs.provider/set-writeable.error=Could not set the writeable flag of file "{0}".
 vfs.provider/set-readable.error=Could not set the readable flag of file "{0}".
diff --git a/commons-vfs2/src/main/java/org/apache/commons/vfs2/filter/SymbolicLinkFileFilter.java b/commons-vfs2/src/main/java/org/apache/commons/vfs2/filter/SymbolicLinkFileFilter.java
new file mode 100644
index 0000000..4ca078c
--- /dev/null
+++ b/commons-vfs2/src/main/java/org/apache/commons/vfs2/filter/SymbolicLinkFileFilter.java
@@ -0,0 +1,89 @@
+/*
+ * 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.commons.vfs2.filter;
+
+import java.io.Serializable;
+
+import org.apache.commons.vfs2.FileFilter;
+import org.apache.commons.vfs2.FileSelectInfo;
+import org.apache.commons.vfs2.FileSystemException;
+
+/**
+ * This filter accepts <code>File</code>s that are symbolic links.
+ * <p>
+ * Example, showing how to print out a list of the current directory's
+ * <i>symbolic link</i> files:
+ * </p>
+ *
+ * <pre>
+ * FileSystemManager fsManager = VFS.getManager();
+ * FileObject dir = fsManager.toFileObject(new File(&quot;.&quot;));
+ * FileObject[] files = dir.findFiles(new FileFilterSelector(SymbolicLinkFileFilter.SYMBOLIC));
+ * for (int i = 0; i &lt; files.length; i++) {
+ *     System.out.println(files[i]);
+ * }
+ * </pre>
+ *
+ * <p>
+ * Example, showing how to print out a list of the current directory's
+ * <i>actual</i> (i.e. symbolic link) files:
+ * </p>
+ *
+ * <pre>
+ * FileSystemManager fsManager = VFS.getManager();
+ * FileObject dir = fsManager.toFileObject(new File(&quot;.&quot;));
+ * FileObject[] files = dir.findFiles(new FileFilterSelector(SymbolicLinkFileFilter.ACTUAL));
+ * for (int i = 0; i &lt; files.length; i++) {
+ *     System.out.println(files[i]);
+ * }
+ * </pre>
+ *
+ * @since 2.4
+ */
+public class SymbolicLinkFileFilter implements FileFilter, Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /** Singleton instance of <i>hidden</i> filter. */
+    public static final FileFilter SYMBOLIC = new SymbolicLinkFileFilter();
+
+    /** Singleton instance of <i>visible</i> filter. */
+    public static final FileFilter ACTUAL = new NotFileFilter(SYMBOLIC);
+
+    /**
+     * Restrictive constructor.
+     */
+    protected SymbolicLinkFileFilter() {
+    }
+
+    /**
+     * Checks to see if the file is a symbolic link. Non existing files won't be accepted.
+     *
+     * @param fileInfo the file to check
+     *
+     * @return {@code true} if the file is <i>symbolic link</i>, otherwise {@code false}.
+     * @throws FileSystemException Thrown for file system errors.
+     */
+    @Override
+    public boolean accept(final FileSelectInfo fileInfo) throws FileSystemException {
+        if (!fileInfo.getFile().exists()) {
+            return false;
+        }
+        return fileInfo.getFile().isSymbolicLink();
+    }
+
+}
diff --git a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/AbstractFileObject.java b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/AbstractFileObject.java
index 1c73172..1fac6f0 100644
--- a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/AbstractFileObject.java
+++ b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/AbstractFileObject.java
@@ -732,6 +732,21 @@ public abstract class AbstractFileObject<AFS extends AbstractFileSystem> impleme
     }
 
     /**
+     * Determines if this file is a symbolic link. Is only called if {@link #doGetType} does not return
+     * {@link FileType#IMAGINARY}.
+     * <p>
+     * This implementation always returns false.
+     * </p>
+     *
+     * @return true if the file is readable, false otherwise.
+     * @throws Exception if an error occurs.
+     * @since 2.4
+     */
+    protected boolean doIsSymbolicLink() throws Exception {
+        return false;
+    }
+
+    /**
      * Determines if this file can be written to. Is only called if {@link #doGetType} does not return
      * {@link FileType#IMAGINARY}.
      * <p>
@@ -1549,6 +1564,22 @@ public abstract class AbstractFileObject<AFS extends AbstractFileSystem> impleme
     }
 
     /**
+     * Determines if this file can be read.
+     *
+     * @return true if the file can be read, false otherwise.
+     * @throws FileSystemException if an error occurs.
+     * @since 2.4
+     */
+    @Override
+    public boolean isSymbolicLink() throws FileSystemException {
+        try {
+            return exists() ? doIsSymbolicLink() : false;
+        } catch (final Exception exc) {
+            throw new FileSystemException("vfs.provider/check-is-symbolic-link.error", fileName, exc);
+        }
+    }
+
+    /**
      * Determines if this file can be written to.
      *
      * @return true if the file can be written to, false otherwise.
diff --git a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/local/LocalFile.java b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/local/LocalFile.java
index bcb770d..e0dcf59 100644
--- a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/local/LocalFile.java
+++ b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/local/LocalFile.java
@@ -22,6 +22,7 @@ import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.nio.file.Files;
 
 import org.apache.commons.vfs2.FileObject;
 import org.apache.commons.vfs2.FileSystemException;
@@ -202,6 +203,15 @@ public class LocalFile extends AbstractFileObject<LocalFileSystem> {
     }
 
     /**
+     * Determines if this file is a symbolic link.
+     * @since 2.4
+     */
+    @Override
+    protected boolean doIsSymbolicLink() throws FileSystemException {
+        return Files.isSymbolicLink(file.toPath());
+    }
+
+    /**
      * Returns the children of the file.
      */
     @Override
diff --git a/commons-vfs2/src/test/java/org/apache/commons/vfs2/filter/BaseFilterTest.java b/commons-vfs2/src/test/java/org/apache/commons/vfs2/filter/BaseFilterTest.java
index 2f4cdb3..7435e9d 100644
--- a/commons-vfs2/src/test/java/org/apache/commons/vfs2/filter/BaseFilterTest.java
+++ b/commons-vfs2/src/test/java/org/apache/commons/vfs2/filter/BaseFilterTest.java
@@ -28,6 +28,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipOutputStream;
 
@@ -73,6 +74,11 @@ public abstract class BaseFilterTest {
                         throw new RuntimeException(ex);
                     }
                 }
+                
+                @Override
+                public String toString() {
+                    return Objects.toString(fileObject);
+                }
             };
         } catch (final FileSystemException ex) {
             throw new RuntimeException(ex);
diff --git a/commons-vfs2/src/test/java/org/apache/commons/vfs2/filter/SymbolicLinkFileFilterTest.java b/commons-vfs2/src/test/java/org/apache/commons/vfs2/filter/SymbolicLinkFileFilterTest.java
new file mode 100644
index 0000000..ff010d2
--- /dev/null
+++ b/commons-vfs2/src/test/java/org/apache/commons/vfs2/filter/SymbolicLinkFileFilterTest.java
@@ -0,0 +1,136 @@
+/*
+ * 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.commons.vfs2.filter;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.vfs2.FileFilter;
+import org.apache.commons.vfs2.FileFilterSelector;
+import org.apache.commons.vfs2.FileObject;
+import org.apache.commons.vfs2.FileSelectInfo;
+import org.apache.commons.vfs2.FileSystemException;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * Test for {@link SymbolicLinkFileFilter}.
+ * <p>
+ * On Windows, in order for this test to pass, you MUST run the VM with admin rights.
+ * </p>
+ * <p>
+ * To enable this test set the system property "SymbolicLinkFileFilterTest.Enable" to "true".
+ * 
+ * <p>
+ * To run only this test with Maven:
+ * </p>
+ * 
+ * <pre>
+ * mvn test -Dtest=SymbolicLinkFileFilterTest -pl commons-vfs2 -DSymbolicLinkFileFilterTest.Enable=true
+ * </pre>
+ */
+// CHECKSTYLE:OFF Test code
+public class SymbolicLinkFileFilterTest extends BaseFilterTest {
+
+    private static File testDir;
+
+    private static File targetFile;
+
+    private static FileSelectInfo targetFileInfo;
+
+    private static File linkFile;
+
+    private static FileSelectInfo linkFileInfo;
+
+    private static File notExistingFile;
+
+    private static FileSelectInfo notExistingFileInfo;
+
+    private static File zipFile;
+
+    private static FileObject zipFileObject;
+
+    @BeforeClass
+    public static void beforeClass() throws IOException {
+        testDir = getTestDir(SymbolicLinkFileFilterTest.class.getName());
+        testDir.mkdir();
+
+        linkFile = new File(testDir, "visible.txt");
+        linkFileInfo = createFileSelectInfo(linkFile);
+
+        targetFile = new File(testDir, "symbolic.txt");
+        Files.deleteIfExists(targetFile.toPath());
+        FileUtils.touch(targetFile);
+        Files.createSymbolicLink(linkFile.toPath(), targetFile.toPath());
+        targetFileInfo = createFileSelectInfo(targetFile);
+
+        notExistingFile = new File(testDir, "not-existing-file.txt");
+        notExistingFileInfo = createFileSelectInfo(notExistingFile);
+
+        // Zip the test directory
+        zipFile = new File(getTempDir(), SymbolicLinkFileFilterTest.class.getName() + ".zip");
+        zipDir(testDir, "", zipFile);
+        zipFileObject = getZipFileObject(zipFile);
+    }
+
+    @AfterClass
+    public static void afterClass() throws IOException {
+        Assume.assumeTrue(Boolean.getBoolean(SymbolicLinkFileFilterTest.class.getSimpleName() + ".Enable"));
+        targetFile = null;
+        targetFileInfo = null;
+        linkFile = null;
+        linkFileInfo = null;
+        notExistingFile = null;
+        notExistingFileInfo = null;
+        if (zipFileObject != null) {
+            zipFileObject.close();
+        }
+        FileUtils.deleteQuietly(zipFile);
+        zipFile = null;
+        FileUtils.deleteDirectory(testDir);
+        testDir = null;
+    }
+
+    @Test
+    public void testAcceptActual() throws FileSystemException {
+        final FileFilter testee = SymbolicLinkFileFilter.ACTUAL;
+        Assert.assertTrue(targetFileInfo.getBaseFolder().exists());
+        Assert.assertTrue(targetFileInfo.getFile().exists());
+        Assert.assertTrue(targetFileInfo.toString(), testee.accept(targetFileInfo));
+        Assert.assertTrue(notExistingFileInfo.toString(), testee.accept(notExistingFileInfo));
+    }
+
+    @Test
+    public void testAcceptSymbolic() throws FileSystemException {
+        final FileFilter testee = SymbolicLinkFileFilter.SYMBOLIC;
+        Assert.assertTrue(linkFileInfo.toString(), testee.accept(linkFileInfo));
+        Assert.assertFalse(notExistingFileInfo.toString(), testee.accept(notExistingFileInfo));
+    }
+
+    @Test
+    public void testZipFile() throws FileSystemException {
+        FileObject[] files = zipFileObject.findFiles(new FileFilterSelector(SymbolicLinkFileFilter.SYMBOLIC));
+        Assert.assertEquals(0, files.length);
+    }
+
+}
+// CHECKSTYLE:ON
diff --git a/commons-vfs2/src/test/java/org/apache/commons/vfs2/test/ProviderReadTests.java b/commons-vfs2/src/test/java/org/apache/commons/vfs2/test/ProviderReadTests.java
index ec750fc..896d30f 100644
--- a/commons-vfs2/src/test/java/org/apache/commons/vfs2/test/ProviderReadTests.java
+++ b/commons-vfs2/src/test/java/org/apache/commons/vfs2/test/ProviderReadTests.java
@@ -210,6 +210,14 @@ public class ProviderReadTests extends AbstractProviderTestCase {
     }
 
     /**
+     * Tests that test read folder is not a symbolic link.
+     */
+    public void testFolderIsSymbolicLink() throws Exception {
+        final FileObject folder = getReadFolderDir1();
+        Assert.assertFalse(folder.isSymbolicLink());
+    }
+
+    /**
      * Tests that test read folder is readable.
      */
     public void testFolderIsReadable() throws Exception {
diff --git a/commons-vfs2/src/test/java/org/apache/commons/vfs2/test/ProviderWriteTests.java b/commons-vfs2/src/test/java/org/apache/commons/vfs2/test/ProviderWriteTests.java
index 3677fe5..9a8e0fe 100644
--- a/commons-vfs2/src/test/java/org/apache/commons/vfs2/test/ProviderWriteTests.java
+++ b/commons-vfs2/src/test/java/org/apache/commons/vfs2/test/ProviderWriteTests.java
@@ -111,6 +111,7 @@ public class ProviderWriteTests extends AbstractProviderTestCase {
         assertTrue(file.isFile());
         assertEquals(0, file.getContent().getSize());
         assertFalse(file.isHidden());
+        assertFalse(file.isSymbolicLink());
         assertTrue(file.isReadable());
         assertTrue(file.isWriteable());
 
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index f3c00f5..3be7076 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -137,6 +137,9 @@ The <action> type attribute can be add,update,fix,remove.
       <action issue="VFS-720" dev="ggregory" type="add" due-to="Boris Petrov">
         Implement Closeable for RandomAccessContent #66.
       </action>
+      <action issue="VFS-721" dev="ggregory" type="add" due-to="Gary Gregory">
+        Add support for symbolic links for the local file system and add FileObject#isSymbolicLink().
+      </action>
     </release>
     <release version="2.3" date="2019-02-01" description="New features and bug fix release.">
       <action issue="VFS-645" dev="ggregory" type="fix">