Author: krosenvold
Date: Fri Jun 19 18:00:44 2015
New Revision: 1686472
URL: http://svn.apache.org/r1686472
Log:
IO-452 broken symlink support
Patch by David Standish.
Also added reflection-based java7 symlink support from maven code (original
author is me)
Added:
commons/proper/io/trunk/src/main/java/org/apache/commons/io/Java7Support.java
Modified:
commons/proper/io/trunk/src/main/java/org/apache/commons/io/FileUtils.java
commons/proper/io/trunk/src/test/java/org/apache/commons/io/FileUtilsCleanSymlinksTestCase.java
Modified:
commons/proper/io/trunk/src/main/java/org/apache/commons/io/FileUtils.java
URL:
http://svn.apache.org/viewvc/commons/proper/io/trunk/src/main/java/org/apache/commons/io/FileUtils.java?rev=1686472&r1=1686471&r2=1686472&view=diff
==============================================================================
--- commons/proper/io/trunk/src/main/java/org/apache/commons/io/FileUtils.java
(original)
+++ commons/proper/io/trunk/src/main/java/org/apache/commons/io/FileUtils.java
Fri Jun 19 18:00:44 2015
@@ -3009,8 +3009,10 @@ public class FileUtils {
* Will not return true if there is a Symbolic Link anywhere in the path,
* only if the specific file is.
* <p/>
- * <b>Note:</b> the current implementation always returns {@code false} if
the system
- * is detected as Windows using {@link FilenameUtils#isSystemWindows()}
+ * When using jdk1.7, this method delegates to {@code boolean
java.nio.file.Files.isSymbolicLink(Path path)}
+ *
+ * <b>Note:</b> the current implementation always returns {@code false} if
running on
+ * jkd1.6 and the system is detected as Windows using {@link
FilenameUtils#isSystemWindows()}
* <p/>
* For code that runs on Java 1.7 or later, use the following method
instead:
* <br>
@@ -3021,6 +3023,11 @@ public class FileUtils {
* @since 2.0
*/
public static boolean isSymlink(final File file) throws IOException {
+ if ( Java7Support.isAtLeastJava7() )
+ {
+ return Java7Support.isSymLink( file );
+ }
+
if (file == null) {
throw new NullPointerException("File must not be null");
}
@@ -3036,10 +3043,41 @@ public class FileUtils {
}
if
(fileInCanonicalDir.getCanonicalFile().equals(fileInCanonicalDir.getAbsoluteFile()))
{
- return false;
+ return isBrokenSymlink(file);
} else {
return true;
}
}
+ /**
+ * Determines if the specified file is possibly a broken symbolic link.
+ *
+ * @param file the file to check
+
+ * @return true if the file is a Symbolic Link
+ * @throws IOException if an IO error occurs while checking the file
+ */
+ private static boolean isBrokenSymlink(final File file) throws IOException
{
+ // if file exists then if it is a symlink it's not broken
+ if (file.exists()) {
+ return false;
+ }
+ // a broken symlink will show up in the list of files of its parent
directory
+ final File canon = file.getCanonicalFile();
+ File parentDir = canon.getParentFile();
+ if (parentDir == null || !parentDir.exists()) {
+ return false;
+ }
+
+ // is it worthwhile to create a FileFilterUtil method for this?
+ // is it worthwhile to create an "identity" IOFileFilter for this?
+ File[] fileInDir = parentDir.listFiles(
+ new FileFilter() {
+ public boolean accept(File aFile) {
+ return aFile.equals(canon);
+ }
+ }
+ );
+ return fileInDir.length > 0;
+ }
}
Added:
commons/proper/io/trunk/src/main/java/org/apache/commons/io/Java7Support.java
URL:
http://svn.apache.org/viewvc/commons/proper/io/trunk/src/main/java/org/apache/commons/io/Java7Support.java?rev=1686472&view=auto
==============================================================================
---
commons/proper/io/trunk/src/main/java/org/apache/commons/io/Java7Support.java
(added)
+++
commons/proper/io/trunk/src/main/java/org/apache/commons/io/Java7Support.java
Fri Jun 19 18:00:44 2015
@@ -0,0 +1,199 @@
+package org.apache.commons.io;
+
+/*
+ * 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.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Java7 feature detection and reflection based feature access.
+ *
+ * Taken from maven-shared-utils, only for private usage until we go full java7
+ */
+class Java7Support
+{
+
+ private static final boolean IS_JAVA7;
+
+ private static Method isSymbolicLink;
+
+ private static Method delete;
+
+ private static Method toPath;
+
+ private static Method exists;
+
+ private static Method toFile;
+
+ private static Method readSymlink;
+
+ private static Method createSymlink;
+
+ private static Object emptyLinkOpts;
+
+ private static Object emptyFileAttributes;
+
+ static
+ {
+ boolean isJava7x = true;
+ try
+ {
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ Class<?> files = cl.loadClass( "java.nio.file.Files" );
+ Class<?> path = cl.loadClass( "java.nio.file.Path" );
+ Class<?> fa = cl.loadClass(
"java.nio.file.attribute.FileAttribute" );
+ Class<?> linkOption = cl.loadClass( "java.nio.file.LinkOption" );
+ isSymbolicLink = files.getMethod( "isSymbolicLink", path );
+ delete = files.getMethod( "delete", path );
+ readSymlink = files.getMethod( "readSymbolicLink", path );
+
+ emptyFileAttributes = Array.newInstance( fa, 0 );
+ final Object o = emptyFileAttributes;
+ createSymlink = files.getMethod( "createSymbolicLink", path, path,
o.getClass() );
+ emptyLinkOpts = Array.newInstance( linkOption, 0 );
+ exists = files.getMethod( "exists", path, emptyLinkOpts.getClass()
);
+ toPath = File.class.getMethod( "toPath" );
+ toFile = path.getMethod( "toFile" );
+ }
+ catch ( ClassNotFoundException e )
+ {
+ isJava7x = false;
+ }
+ catch ( NoSuchMethodException e )
+ {
+ isJava7x = false;
+ }
+ IS_JAVA7 = isJava7x;
+ }
+
+ public static boolean isSymLink( File file )
+ {
+ try
+ {
+ Object path = toPath.invoke( file );
+ return (Boolean) isSymbolicLink.invoke( null, path );
+ }
+ catch ( IllegalAccessException e )
+ {
+ throw new RuntimeException( e );
+ }
+ catch ( InvocationTargetException e )
+ {
+ throw new RuntimeException( e );
+ }
+ }
+
+
+ public static File readSymbolicLink( File symlink )
+ throws IOException
+ {
+ try
+ {
+ Object path = toPath.invoke( symlink );
+ Object resultPath = readSymlink.invoke( null, path );
+ return (File) toFile.invoke( resultPath );
+ }
+ catch ( IllegalAccessException e )
+ {
+ throw new RuntimeException( e );
+ }
+ catch ( InvocationTargetException e )
+ {
+ throw new RuntimeException( e );
+ }
+ }
+
+
+ public static boolean exists( File file )
+ throws IOException
+ {
+ try
+ {
+ Object path = toPath.invoke( file );
+ final Object invoke = exists.invoke( null, path, emptyLinkOpts );
+ return (Boolean) invoke;
+ }
+ catch ( IllegalAccessException e )
+ {
+ throw new RuntimeException( e );
+ }
+ catch ( InvocationTargetException e )
+ {
+ throw (RuntimeException) e.getTargetException();
+ }
+
+ }
+
+ public static File createSymbolicLink( File symlink, File target )
+ throws IOException
+ {
+ try
+ {
+ if ( !exists( symlink ) )
+ {
+ Object link = toPath.invoke( symlink );
+ Object path = createSymlink.invoke( null, link, toPath.invoke(
target ), emptyFileAttributes );
+ return (File) toFile.invoke( path );
+ }
+ return symlink;
+ }
+ catch ( IllegalAccessException e )
+ {
+ throw new RuntimeException( e );
+ }
+ catch ( InvocationTargetException e )
+ {
+ final Throwable targetException = e.getTargetException();
+ throw (IOException) targetException;
+ }
+
+ }
+ /**
+ * Performs a nio delete
+ * @param file the file to delete
+ * @throws IOException
+ */
+ public static void delete( File file )
+ throws IOException
+ {
+ try
+ {
+ Object path = toPath.invoke( file );
+ delete.invoke( null, path );
+ }
+ catch ( IllegalAccessException e )
+ {
+ throw new RuntimeException( e );
+ }
+ catch ( InvocationTargetException e )
+ {
+ throw (IOException) e.getTargetException();
+ }
+ }
+
+ public static boolean isAtLeastJava7()
+ {
+ return IS_JAVA7;
+ }
+
+}
Modified:
commons/proper/io/trunk/src/test/java/org/apache/commons/io/FileUtilsCleanSymlinksTestCase.java
URL:
http://svn.apache.org/viewvc/commons/proper/io/trunk/src/test/java/org/apache/commons/io/FileUtilsCleanSymlinksTestCase.java?rev=1686472&r1=1686471&r2=1686472&view=diff
==============================================================================
---
commons/proper/io/trunk/src/test/java/org/apache/commons/io/FileUtilsCleanSymlinksTestCase.java
(original)
+++
commons/proper/io/trunk/src/test/java/org/apache/commons/io/FileUtilsCleanSymlinksTestCase.java
Fri Jun 19 18:00:44 2015
@@ -203,6 +203,25 @@ public class FileUtilsCleanSymlinksTestC
assertFalse(FileUtils.isSymlink(randomFile));
}
+ public void testIdentifiesBrokenSymlinkFile() throws Exception {
+ if (System.getProperty("os.name").startsWith("Win")) {
+ // cant create symlinks in windows.
+ return;
+ }
+
+ final File noexistFile = new File(top, "noexist");
+ final File symlinkFile = new File(top, "fakeinner");
+ final File badSymlinkInPathFile = new File(symlinkFile, "fakeinner");
+ final File noexistParentFile = new File("noexist", "file");
+
+ setupSymlink(noexistFile, symlinkFile);
+
+ assertTrue(FileUtils.isSymlink(symlinkFile));
+ assertFalse(FileUtils.isSymlink(noexistFile));
+ assertFalse(FileUtils.isSymlink(noexistParentFile));
+ assertFalse(FileUtils.isSymlink(badSymlinkInPathFile));
+ }
+
public void testCorrectlyIdentifySymlinkWithParentSymLink() throws
Exception {
if (System.getProperty("os.name").startsWith("Win")) {
// cant create symlinks in windows.