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 <gardgreg...@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(".")); + * FileObject[] files = dir.findFiles(new FileFilterSelector(SymbolicLinkFileFilter.SYMBOLIC)); + * for (int i = 0; i < 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(".")); + * FileObject[] files = dir.findFiles(new FileFilterSelector(SymbolicLinkFileFilter.ACTUAL)); + * for (int i = 0; i < 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">