If I call one of the methods File.mkdir() or File.mkdirs() in Java, and it returns false, is there a way to know why was the directory not created?
Not really, no. If a SecurityException is NOT thrown, then the most likely cause is a typo in the path, meaning you've accidentally specified a parent path to the new directories that is somehow invalid.
I don't suppose you have it wrapped in a try { ... } catch (Exception e) block, where you don't realize a SecurityException is being thrown, because you're catching an ancestor of SecurityException, do you?
If you have a high belief that everything looks right, and it still fails, I suppose you could simply put it in a loop to retry, say, three times. If it still fails, and depending on your application, you might raise some kind of alert at the UI level, or log the error in a log file (assuming you can write to it).
I suppose it's possible that some deeper I/O issue is preventing it from working, but beyond simply notifying the user of a failure there isn't much you can (or really should) do at an application level. If there's something deeper in the I/O wrong, that's more likely a problem with the system/hardware/OS, or something completely wonky that you have no control over like a subsystem/service crash.
...and if that's happening, that's the responsibility of the IT guy to fix, not your application. Unless of course your app is somehow causing the crash.
I had a mkdirs() failure on windows on a UNC path.
The code looks like this:
public File getOldDirectoryPath(String root, String name)
{
File fulldir = new File(root, name)
boolean created = false
int retry = 0
while (!created) {
retry++
if (!(created = fulldir.exists())) {
if (20 == retry) break
if (!fulldir.mkdirs()) {
sleep(100)
fulldir = new File(root, name)
}
}
}
return fulldir.exists() ? fulldir : null
}
There appears to be some sort of caching involved where exists() returns false (does not exists) but the mkdir on the file system fails because it does exist. Recreating the File() entry or lengthing the timeout did not make a difference.
I discovered a plugin on elasticsearch to fix a SMB problem on Windows. Researching the solution, it uses nio.file instead of io.File. Rewriting the function fixed the issue:
public File getDirectoryPath(String root, String name)
{
Path fulldir = Paths.get(root, name)
boolean created = false
int retry = 0
while (!created) {
retry++
if (!(created = Files.isDirectory(fulldir))) {
if (20 == retry) break
try {
Files.createDirectories(fulldir)
} catch (Throwable thx) {
// error handling
}
}
}
return fulldir.toFile()
}
createDirectories() sometimes fail, but recovers where mkdirs() does not.
Related
I'm creating a "File Manager" to practice my IO skills. If I'm creating a file, how would I handle an error if file#createNewFile() returns false?
#Override
public void run() {
File file = new File(src + "\\" + srcName);
try {
if (file.createNewFile()) {
MessageHandler.createSuccess(comp);
} else {
throw new IOException();
}
} catch (IOException e) {
MessageHandler.error500(comp);
}
}
Right now I'm simply throwing an IOException to be caught in the next block, but I'm not sure if that is the best idea. It should be fine on the client end but when debugging I would have no idea what went wrong internally.
It depends. I guess there is no single clear answer to this, so other members of the community might view this differently than I do.
According to the Javadoc, createNewFile returns false only if the file already exists.
Therefore, in cases where the file should not be present whatsoever, I also just throw an IOException.
If I have a reason to assume the file could be present, the user interacting with the program should be notified of this problem in some way. How exactly you do this depends on the UI of your program.
The code snippet you showed us does not make much sense. Throwing an exception, just to catch it immediately, isn't very performant (unless the clever Java optimizers eliminate the costly operations).
Your current run() method does:
MessageHandler.createSuccess() if the file didn't exist and could be created (file.createNewFile() returned true).
MessageHandler.error500() if the file didn't exist, but could not be created, e.g. because of not having the necessary permissions (file.createNewFile() threw an IOException).
MessageHandler.error500() if the file already existed (file.createNewFile() returned true). [Are you sure that already having the file is an error?]
For the case number 3, creating, throwing and catching an IOException does not serve any useful purpose (other than guiding the code into the catch clause).
I'd simply rewrite the code to something like
#Override
public void run() {
File file = new File(src + "\\" + srcName);
try {
if (file.createNewFile()) {
MessageHandler.createSuccess(comp);
} else {
MessageHandler.error500(comp);
}
} catch (IOException e) {
MessageHandler.error500(comp);
}
}
You might even find a result code different from 500, better representing the cause of the failure.
Exceptions are a very good way for one method to communicate to the other methods up its caller hierarchy that it wasn't able to do its job. Throwing and catching an exception within one method rarely is a good idea.
void appendFile() throws IOException{
FileWriter print = new FileWriter(f, true);
String info = GetData.getWord("Write desired info to File");
print.append(" "); //Write Data
print.append(info);
System.out.println("this executes");
print.flush();
print.close();
}
boolean fileExist(){
return f.exists();
}
try{
if (f.fileExist())
f.appendFile();
else {
throw new IOException();
}
}
catch(IOException e) {
e.printStackTrace();
}
I'm not sure if the ecxeption is well handeled? The FileWriter is not going to be created if there is a fileNotFoundException, therefore don't need to be closed. However, is it possible that this code throws another kind of IOException after the file was opened?, and in that case do I need a finally block to close the file.
No.
It doesn't safely close the resource
The general rule is, if you call a constructor of an object that represents a closable resource, or a method that returns one which documents that this counts as 'opening the resource', which often but not always includes new being part of the method name (examples: socket.getInputStream(), Files.newInputStream), then you are responsible for closing it.
The problem is, what if an exception occurs? So, you have to use try/finally, except that's a mouthful, so there's a convenient syntax for this.
The appendFile method should use it; it isn't, that makes it bad code. This is correct:
try (FileWriter print = new FileWriter(f, true)) {
String info = GetData.getWord("Write desired info to File");
print.append(" "); //Write Data
print.append(info);
System.out.println("this executes");
}
Not how it is not neccessary to flush before close (close implies flush), and in this case, not neccessary to close() - the try construct does it for you. It also does it for you if you get out of the {} of the try via a return statement, via control flow (break), or via an exception, or just by running to the } and exiting normally. No matter how - the resource is closed. As it should be.
It throws description-less exceptions
else throw new IOException(); is no good; add a description that explains why the exception happened. throw new IOException("File not found") is better, but throw new FileNotFoundException(f.toString()) is even better: The message should convey useful information and nothing more than that (in other words, throw new IOException("Something went wrong") is horrible, don't do that, that message adds no useful information), should not end in punctuation (throw new IOException("File not found!") is bad), and should throw a type that is most appropriate (if the file isn't there, FileNotFoundException, which is a subtype of IOException, is more appropriate, obviously).
It commits the capital offense
You should not ever write a catch block whose contents are just e.printStackTrace();. This is always wrong.
Here's what you do with a checked exception:
First, think of what the exception means and whether the nature of your method inherently implies that this exception can occur (vs. that it is an implementation detail). In this case, you didn't show us what the method containing that try/catch stuff even does. But let's say it is called appendFile, obviously a method that includes the text 'file' does I/O, and therefore, that method should be declared with throws IOException. It's not an implementation detail that a method named appendFile interacts with files. It is its nature.
This is somewhat in the eye of the beholder. Imagine a method named saveGame. That's less clear; perhaps the mechanism to save may involve a database instead, in which case SQLException would be normal. That's an example of a method where 'it interacts with the file system' is an implementation detail.
The problem that the exception signals is logical, but needs to be more abstract.
See the above: A save file system can obviously fail to save, but the exact nature of the error is abstract: If the save file system is DB-based, errors would show up in the form of an SQLException; if a file system, IOException, etcetera. However, the idea that saving may fail, and that the code that tried to save has a reasonable chance that it can somewhat recover from this, is obvious. If it's a game, there's a user interface; you should most absolutely TELL the player that saving failed, instead of shunting some stack trace into sysout which they probably never even look at! Telling the user that something failed is one way of handling things, not always the best, but here it applies).
In such cases, make your own exception type and throw that, using the wrapper constructor:
public class SaveException extends Exception {
public SaveException(Throwable cause) {
super(cause);
}
}
// and to use:
public void save() throws SaveException {
try {
try (OutputStream out = Files.newOutputStream(savePath)) {
game.saveTo(out);
}
} catch (IOException e) {
throw new SaveException(e);
}
}
If neither applies, then perhaps the exception is either essentially not handleable or not expectable or nearly always a bug. For example, writing to an outputstream that you know is a ByteArrayOutputStream (which can't throw), trying to load the UTF-8 charset (which is guaranteed by the JVM spec and therefore cannot possibly throw NoSuchCharsetException) - those are not expectable. Something like Pattern.compile("Some-regexp-here") can fail (not all strings are valid regexps), but as the vast majority of regexes in java are literals written by a programmer, any error in them is therefore neccessarily a bug. Those, too, are properly done as RuntimeExceptions (which are exceptions you don't have to catch or list in your throws line). Not handleables are mostly an application logic level thing. All fair game for runtimeexceptions. Make your own or use something that applies:
public void save(int saveSlot) {
if (saveSlot < 1 || saveSlot > 9) throw new IllegalArgumentException("Choose a saveslot from 1 to 9");
// ... rest of code.
}
This really feels like door number one: Whatever method this is in probably needs to be declared as throws IOException and do no catching or trying at all.
Minor nit: Uses old API
There's new API for file stuff in the java.nio.file package. It's 'better', in that the old API does a bunch of bad things, such as returning failure by way of a boolean flag instead of doing it right (by throwing an exception), and the new API has far more support for various bits and bobs of what file systems do, such as support for file links and creation timestamps.
In Windows a file may not be writable because the user simply doesn't have permission to modify the file due to Access Control Lists permissions, or just because the read only attribute is set for the file.
My application is written in Java and either of these conditions could cause Files.isWritable(file) to fail, but how can I determine which condition caused the failure, specifically I just want to know if the read only attribute is set.
I note that there is a File.setReadOnly() method (as well as File.setWritable()), and I assume on Windows this would just set the attribute, but there doesn;t seem to be a File.isReadOnly() method.
I used this method to check if read only, (it uses custom method Platform .ifWindows() to only run on Windows, but you could use alternatives).
private boolean isReadOnlyFile(Path file)
{
if(Platform.isWindows())
{
if (!file.toFile().canWrite())
{
DosFileAttributes dosAttr;
try
{
dosAttr = Files.readAttributes(file, DosFileAttributes.class);
if(dosAttr.isReadOnly())
{
return true;
}
}
catch (IOException e)
{
}
}
}
return false;
}
Use the canWrite() method:
if(file.canWrite()) {
System.out.println("you can write");
} else {
System.out.println("you cannot write");
}
Useful info:
Returns:
true if and only if the file system actually contains a file denoted by this abstract pathname and the application is allowed to
write to the file; false otherwise.
Throws:
SecurityException - If a security manager exists and its SecurityManager.checkWrite(java.lang.String) method denies write
access to the file
Don't. Just try to open the file for output. If it isn't writable, it will fail. Catch the exception and deal with it then. Don't try to predict the future. You could test it wrong, or it could change between test and use, both ways.
What is the proper treatment for this try and catch to treat issue? The file.exists() will never return an exception
public File getFile(File file) {
if (file.exists()) {
return file;
} else {
throw new RuntimeException("Erro ao obter arquivo.");
}
}
If you're sure that the file will always exist, and that completely grinding your application to a halt with a RuntimeException is the correct behaviour in the unlikely event that the file really doesn't exist, then this is acceptable.
Usually, however, a better approach would be to throw a different exception like CustomFailureException (or whatever you may want to call it), log to output why exactly the application has failed, and exit gracefully. Just in the off-chance that one day that file really doesn't exist for some reason, then you'll know right away what the problem is.
I need to check for file creation permissions for specific directory.
I've tried:
try {
AccessController.checkPermission(new FilePermission("directory_path", "write"));
// Have permission
} catch (SecurityException e) {
// Doesn't have permission
}
... but this always throws SecurityException (as far as I've understood this checks not underlying fs permissions but some JVM settings that should be configured explicitly).
Another possible way was to use something like this:
File f = new File("directory_path");
if(f.canWrite()) {
// Have permission
} else {
// Doesn't have permission
}
... but this returns true even if I cannot create file in specified directory (For example I cannot create file in "c:\" when I run my app under user without admin privileges, but f.canWrite() returns true).
In the end I've done hack similar to this:
File f = new File("directory_path");
try {
File.createTempFile("check", null, f).delete();
// Have permission
} catch (IOException e) {
// Doesn't have permission
}
... but this may serve only as a temporary solution since I need to get such permissions for almost all folders on client's fs.
Does anybody know how to get REAL file creation permissions nicely, without causing performance issues and hacks described above?
The best way to test the availability/usability of any resource is to try to use it. In this case, new FileOutputStream(new File(dir, name)). It will throw an IOException if the directory isn't writable, or the file already exists and isn't writable, etc etc etc. The operating system already has to do all those checks when you execute that code: there is no point in trying to duplicate it all, and even if you get it 100% right, which is unlikely, you are still introducing a timing window in which the previously 'true' condition can become 'false' due to asynchronous activity. You have to catch exceptions from the file creation anyway, and write code in the catch block to handle them: why write all that twice?