Re: Bug or change in behavoir ? Commons IO

Dave Garratt <[email protected]> Tue, 31 Dec 2024 12:02:50 +0000
Newsgroups gmane.comp.jakarta.commons.user
Message-ID <[email protected]>
I have created the following function which now gives me the required feedback independently of Commons IO, so I will now be able to migrate to 2.18.0

The function below is actually giving me more feedback about various scenarios which can go wrong so maybe this was a blessing in disguise.

Dave

import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.DosFileAttributes;
import java.nio.file.attribute.PosixFileAttributes;
import java.nio.file.attribute.PosixFilePermission;
import java.util.Set;

public class RemoteShareChecker
{

	private String errorMessage = "";

	private void setErrorMessage(String message)
	{
		this.errorMessage = message;
	}

	public String getErrorMessage()
	{
		return this.errorMessage;
	}

	public boolean isValidPath(String path)
	{
		boolean result = true;

		Path remotePath = Paths.get(path);

		try
		{
			Files.readAttributes(remotePath, BasicFileAttributes.class);
			setErrorMessage("File is accessible: " + remotePath);

			if (Files.isRegularFile(remotePath))
			{
				if (canDelete(remotePath))
				{
					setErrorMessage("The file can be deleted.");
				}
				else
				{
					setErrorMessage("The file cannot be deleted due to permission or other restrictions.");
					result = false;
				}
			}

			if (Files.isRegularFile(remotePath))
			{
				if (isFileLocked(remotePath))
				{
					setErrorMessage("The file is locked by another process.");
					result = false;
				}
				else
				{
					setErrorMessage("The file is not locked.");
				}
			}
		}
		catch (NoSuchFileException e)
		{
			setErrorMessage("The file or directory does not exist: " + e.getFile());
			result = false;
		}
		catch (AccessDeniedException e)
		{
			setErrorMessage("Access denied to the path: " + e.getFile());
			result = false;
		}
		catch (FileSystemException e)
		{
			setErrorMessage("File system error: " + e.getMessage());
			result = false;
		}
		catch (IOException e)
		{
			setErrorMessage("An I/O error occurred: " + e.getMessage());
			result = false;
		}

		return result;
	}

	private boolean canDelete(Path path)
	{
		try
		{
			Path parentDir = path.getParent();

			// Check if the parent directory is writable
			if (!Files.isWritable(parentDir))
			{
				setErrorMessage("Parent directory is not writable: " + parentDir);
				return false;
			}

			// Platform-specific writable check
			if (!System.getProperty("os.name").toLowerCase().contains("win"))
			{
				PosixFileAttributes posixAttributes = Files.readAttributes(path, PosixFileAttributes.class);
				Set<PosixFilePermission> permissions = posixAttributes.permissions();
				if (!permissions.contains(PosixFilePermission.OWNER_WRITE))
				{
					setErrorMessage("File is not writable according to POSIX permissions: " + path);
					return false;
				}
			}
			else
			{
				DosFileAttributes dosAttributes = Files.readAttributes(path, DosFileAttributes.class);
				if (dosAttributes.isReadOnly())
				{
					setErrorMessage("File is read-only: " + path);
					return false;
				}
			}

			return true;
		}
		catch (IOException e)
		{
			setErrorMessage("Error checking delete permissions: " + e.getMessage());
			return false;
		}
	}

	private boolean isFileLocked(Path path)
	{
		// Attempt to open the file with exclusive access
		try (FileChannel channel = FileChannel.open(path, StandardOpenOption.WRITE))
		{
			// File is not locked if we successfully open it
			return false;
		}
		catch (IOException e)
		{
			setErrorMessage("Error: Unable to open the file for write access. It may be locked.");
			return true;
		}
	}
}




> On 30 Dec 2024, at 22:43, Gary Gregory <[email protected]> wrote:
> 
> We had a unit test missing for this specific use case it seems. Maybe check
> your test coverage (JaCoCo for example).
> 
> Gary
> 
> On Mon, Dec 30, 2024, 5:22 PM Dave Garratt <[email protected]>
> wrote:
> 
>> Well yes I suppose that would suffice - I’m just a little worried about
>> how much code might be broken in my dependencies as well as my own code.
>> 
>> Dave
>> 
>>> On 30 Dec 2024, at 21:28, Ruby Paasche <[email protected]> wrote:
>>> 
>>> How about `dir.exists()` maybe in combination with `dir.isDirectory()`?
>>> 
>>> Dave Garratt <[email protected] <mailto:
>> [email protected]>> schrieb am Mo., 30. Dez. 2024, 22:21:
>>>> I’m not sure if I understand if the usefulness of the behaviour in all
>> versions up to and including 2.17.0 is a bug unless something else is
>> happening which I’m unaware of.
>>>> 
>>>> Assuming the ability to detect a path / network url is no longer valid
>> has gone it begs the question- is there some other mechanism within commons
>> io which can be used to detect the same issue.
>>>> 
>>>> The application which I develop runs as a middleware solution and as a
>> service. When a network url is no longer available I generate an alert via
>> an email.
>>>> 
>>>> My code simply loops around check for the presence of files in a folder
>> constantly.
>>>> 
>>>> With the new behaviour I won’t be able to detect and report on a
>> failure of this kind.
>>>> 
>>>> Thanks
>>>> 
>>>> Dave
>>>> 
>>>> 
>>>> ‑‑​​​​
>>>> Ruby Paasche
>>>> Senior Entwicklerin
>>>> <image493809.png> <https://www.linkedin.com/company/pripares-gmbh>
>>>> PRIPARES Software Solutions GmbH
>>>> Ridlerstraße 57
>>>> 80339 München
>>>> Tel:
>>>> +49 (0)89 45 22 808 ‑ 30
>>>> Email:
>>>> [email protected]
>>>> Web:
>>>> https://pripares.com <https://pripares.com/>
>>>> Handelsregister: Registergericht München HRB 138701
>>>> Sitz der Gesellschaft: München
>>>> Geschäftsführer: Aßmann Christoph, Ertl Andreas
>>>> ​
>>>> ​Diese E‑Mail enthält vertrauliche und/oder rechtlich geschützte
>> Informationen. Wenn Sie nicht der richtige Adressat sind oder diese E‑Mail
>> irrtümlich erhalten haben, informieren Sie bitte sofort den Absender und
>> löschen Sie diese Mail. Das unerlaubte Kopieren sowie die unbefugte
>> Weitergabe dieser Mail und der darin enthaltenen Informationen sind nicht
>> gestattet.
>>>> 
>>>> This e‑mail may contain confidential and/or privileged information. If
>> you are not the intended recipient (or have received this e‑mail in error)
>>>> please notify the sender immediately and delete this e‑mail. Any
>> unauthorized copying, disclosure or distribution of the material in this
>> e‑mail is strictly forbidden.
>>>>> On 30 Dec 2024, at 19:49, Gary D. Gregory <[email protected]
>> <mailto:[email protected]>> wrote:
>>>>> 
>>>>> It looks like this change with by design for
>> https://issues.apache.org/jira/browse/IO-856
>>>>> 
>>>>> Gary
>>>>> 
>>>>>> On 2024/12/30 17:54:17 "Gary D. Gregory" wrote:
>>>>>> Hm, we'll have to look into that one. In the meantime, I added
>>>>>> 
>>>>>> org.apache.commons.io <http://org.apache.commons.io/
>>> .FileUtilsListFilesTest.testListFilesMissing()
>>>>>> 
>>>>>> which asserts the current behavior in git master.
>>>>>> 
>>>>>> TY,
>>>>>> Gary
>>>>>> 
>>>>>>> On 2024/12/30 17:00:17 Dave Garratt wrote:
>>>>>>> 
>>>>>>> I have recently tried updating from Commons IO 2.16.1 to Commons IO
>> 2.18.0
>>>>>>> 
>>>>>>> My java program constantly polls a folder for the presence of
>> files. Normally this will be a remote folder on a windows share.
>>>>>>> 
>>>>>>> In Commons IO 2.16.1 when I use FileUtils.listFiles it would
>> trigger an exception if the folder being polled became unavailable and I
>> would use this to trigger an action.
>>>>>>> 
>>>>>>> In Commons IO 2.18.0 the error does not occur.
>>>>>>> 
>>>>>>> I have created a very simple test program to illustrate this.
>>>>>>> 
>>>>>>> Running this code with Commons IO 2.16.1 and passing an invalid
>> (non existent) path to it gives me the following exception (which in my
>> case is desirable):
>>>>>>> 
>>>>>>> import java.io.File;
>>>>>>> 
>>>>>>> import org.apache.commons.io <http://org.apache.commons.io/
>>> .FileUtils;
>>>>>>> 
>>>>>>> public class Test2161
>>>>>>> {
>>>>>>> 
>>>>>>>   public static void main(String[] args)
>>>>>>>   {
>>>>>>>       File dir = new File("/Users/dave/Downloads/nonexistent");
>>>>>>> 
>>>>>>>       try
>>>>>>>       {
>>>>>>>           FileUtils.listFiles(dir, args, false);
>>>>>>>       }
>>>>>>>       catch (Exception ex)
>>>>>>>       {
>>>>>>>           System.out.println(ex.getMessage());
>>>>>>>           ex.printStackTrace();
>>>>>>>       }
>>>>>>>   }
>>>>>>> }
>>>>>>> 
>>>>>>> java.nio.file.NoSuchFileException: /Users/dave/Downloads/nonexistent
>>>>>>> java.io.UncheckedIOException: java.nio.file.NoSuchFileException:
>> /Users/dave/Downloads/nonexistent
>>>>>>>   at org.apache.commons.io <http://org.apache.commons.io/
>>> .function.Uncheck.wrap(Uncheck.java:339)
>>>>>>>   at org.apache.commons.io <http://org.apache.commons.io/
>>> .function.Uncheck.get(Uncheck.java:199)
>>>>>>>   at org.apache.commons.io <http://org.apache.commons.io/
>>> .FileUtils.listFiles(FileUtils.java:2313)
>>>>>>>   at Test2161.main(Test2161.java:14)
>>>>>>> Caused by: java.nio.file.NoSuchFileException:
>> /Users/dave/Downloads/nonexistent
>>>>>>>   at
>> java.base/sun.nio.fs.UnixException.translateToIOException(UnixException.java:92)
>>>>>>>   at
>> java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:106)
>>>>>>>   at
>> java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:111)
>>>>>>>   at
>> java.base/sun.nio.fs.UnixFileAttributeViews$Basic.readAttributes(UnixFileAttributeViews.java:55)
>>>>>>>   at
>> java.base/sun.nio.fs.UnixFileSystemProvider.readAttributes(UnixFileSystemProvider.java:148)
>>>>>>>   at java.base/java.nio.file.Files.readAttributes(Files.java:1851)
>>>>>>>   at
>> java.base/java.nio.file.FileTreeWalker.getAttributes(FileTreeWalker.java:226)
>>>>>>>   at
>> java.base/java.nio.file.FileTreeWalker.visit(FileTreeWalker.java:277)
>>>>>>>   at
>> java.base/java.nio.file.FileTreeWalker.walk(FileTreeWalker.java:323)
>>>>>>>   at
>> java.base/java.nio.file.FileTreeIterator.<init>(FileTreeIterator.java:71)
>>>>>>>   at java.base/java.nio.file.Files.walk(Files.java:3918)
>>>>>>>   at org.apache.commons.io.file.PathUtils.walk(PathUtils.java:1847)
>>>>>>>   at org.apache.commons.io <http://org.apache.commons.io/
>>> .FileUtils.streamFiles(FileUtils.java:2952)
>>>>>>>   at org.apache.commons.io <http://org.apache.commons.io/
>>> .FileUtils.lambda$listFiles$10(FileUtils.java:2313)
>>>>>>>   at org.apache.commons.io <http://org.apache.commons.io/
>>> .function.Uncheck.get(Uncheck.java:197)
>>>>>>>   ... 2 more
>>>>>>> 
>>>>>>> 
>>>>>>> 
>>>>>>> If however I run the same code with Commons IO 2.18.0 I get
>> nothing, no indication that the path is not valid which make it difficult
>> to determine if the folder is empty or the remote file server is shutdown.
>>>>>>> 
>>>>>>> Am I supposed to use a different way to accomplish the same error
>> condition now ?
>>>>>>> 
>>>>>>> Thanks
>>>>>>> 
>>>>>>> Dave
>>>>>>> 
>>>>>>> 
>>>>>>> 
>> ---------------------------------------------------------------------
>>>>>>> To unsubscribe, e-mail: [email protected]
>> <mailto:[email protected]>
>>>>>>> For additional commands, e-mail: [email protected]
>> <mailto:[email protected]>
>>>>>>> 
>>>>>>> 
>>>>>> 
>>>>>> ---------------------------------------------------------------------
>>>>>> To unsubscribe, e-mail: [email protected] <mailto:
>> [email protected]>
>>>>>> For additional commands, e-mail: [email protected]
>> <mailto:[email protected]>
>>>>>> 
>>>>>> 
>>>>> 
>>>>> ---------------------------------------------------------------------
>>>>> To unsubscribe, e-mail: [email protected] <mailto:
>> [email protected]>
>>>>> For additional commands, e-mail: [email protected]
>> <mailto:[email protected]>
>>>>> 
>>>> 
>>>> ---------------------------------------------------------------------
>>>> To unsubscribe, e-mail: [email protected] <mailto:
>> [email protected]>
>>>> For additional commands, e-mail: [email protected] <mailto:
>> [email protected]>
>>>> 
>> 
>>