Disregarding my last post, I've found the source of the problem. I'm using
a.renameTo(b)
when b doesn't exist. The reason it doesn't exist is because there is a symbolic link so if b is /usr/name/folder/file, then b really is /mnt/MountTest because the symlink is to that directory.
So the question is, is there an alternative way to rename a file in Java using a string value?
If not, how can this rename procedure be done differently?
A rename would rename it... if it were on the same filesystem.
If a renameTo() fails, you'll need to copy it to the new location, then delete the original.
Renaming files is also highly problematic accross file systems. See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4073756. Commenters of the bug report posted some sample code and also pointed out that you can use Process.exec. Both Apache Commons IO and and Google Guava have utilities for safely moving files as well:
http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/io/Files.html#move(java.io.File,%20java.io.File)
https://commons.apache.org/proper/commons-io/javadocs/api-1.4/org/apache/commons/io/FileUtils.html#moveFile(java.io.File,%20java.io.File)
I think you are confusing things. A java.util.File doesn't represent a file on some filesystem. It represents a path to a file.
The problem is not that a symlink is involved; the problem is that you can't atomically rename across filesystems. The meta-problem is that the Java File operations are badly designed, and don't throw proper exceptions, and provide no error codes when something fails!
How about:
a.renameTo(new File("/your/path/here/");
Related
While writing answers around SO, a user tried pointing out that java.io.File should not be used in new code, instead he argues that the the new object java.nio.Files should be used instead; he linked to this article.
Now I have been developing in Java for several years now, and have not heard this argument before; since reading his post I have been searching, and have not found many other sources that confirm this, and personally, I feel like many of the points argued in the article are weak and that if you know how to read them, errors thrown by the File class will generally tell you exactly what the issue is.
As I am continually developing new code my question is this:
Is this an active argument in the Java community? Is Files preferred over File for new code? What are the major advantages / disadvantages between the two?
The documentation that you linked give the answer:
The java.nio.file package defines interfaces and classes for the Java
virtual machine to access files, file attributes, and file systems.
This API may be used to overcome many of the limitations of the
java.io.File class. The toPath method may be used to obtain a Path
that uses the abstract path represented by a File object to locate a
file. The resulting Path may be used with the Files class to provide
more efficient and extensive access to additional file operations,
file attributes, and I/O exceptions to help diagnose errors when an
operation on a file fails.
File has a newer implementation: Path. With a builder Paths.get("..."). And Files has many nice utility functions with better implementations too (move instead of the sometimes failing File.renameTo).
A Path maintains its file system. Hence you can copy out of a zip file system ("jar:file:..... .zip") some path to another file system and vice versa.
File.toPath() may help an incremental transition.
The utilities alone in Files make a move to the newer classes profitable.
I have this model object representing a Java source file.
It has a constructor like so:
private SourceFile(File file)
I want this constructor to actually make sure (as much as it can) that the File it's being given is actually a Java source.
I have a batch operation that takes a lot of text files. Some of them are Java sources, I wan't a good way to differentiate them (other than file extension).
So has anyone been in this situation before and can you recommend a good way to check plausibility (not validity, for a validity check I'd need to compile it) ?
I'd do two things:
Check that the file ends in .java.
Check that the file declares a class that has the same name as the file (see here).
It depends on how accurate you want to be. If you want 100% you have to compile it. If you would be happy with something low you can check printable characters. Reasonable level may be achieved by key work check. And so on...
Use javaparser, on given link is wiki how to use it. But in Java 1.6 the compiler has an API build in the JDK, through it you can access the results of the Java parser.
I used
Files.createTempFile("Hello", "txt");
to create a temporary file and stored the returned Path.
I have an Eclipse IFile resource linked to the temporary file I created:
linkedFile.createLink(tempFile.toUri(), IResource.NONE, null);
If I want to get a Path back from this resource, I call
linkedFile.getLocation().toFile().toPath()
On my local machine, this works 100% fine. But on a remote test machine, I get two different paths:
from Files.createTempFile: C:\Users\USERNA~1\AppData\Local\Temp\Hello3606197456871226795txt
from getLocation().toFile().toPath() C:\Users\Username_Testing\AppData\Local\Temp\Hello3606197456871226795txt
The Folder Username_Testing and only that folder gets turned into a short filename, and only for my direct creation of it as a temporary.
These two paths are not considered equal by Path.equals(...), which is causing a failing of my tests on the remote machine.
In general, this makes me a bit nervous using Path.equals(...) even though in the actual real operation of the application I haven't had any issues yet. Is there a way I can force the system to always use long filenames? Is there something I'm missing that I should be aware of when I do path equality checks, or when converting paths from one form to another?
Update #1: This specific issue is caused by %TEMP% on the target windows machine returning a path using a short filename, that doesn't happen on my local machine. Only test code creates temporary files and folders so this won't affect the real application. The obvious solution to my current problem is fix %TEMP% so the tests run fine in both places, but this solution is not viable in the general sense. It would be nice if there was a way to rectify the situation without modifying the target computer or jumping into native or windows specific code, since I used no such code directly to get both paths.
I found a good, portable solution to my problem, no need to use any platform-specific code. The answer is actually quite simple:
Path.toRealPath()
used something like this:
Path correctedTempFile = tempFile.toRealPath()
Essentially, it is now using the toRealPath() version, which thankfully removes the short filenames, for comparisons against other Paths taken from Eclipse resources. I believe the Eclipse implementation is using only long paths for consistency, so I in turn will use toRealPath to get rid of any potential paths that may use short filenames
This question might help:
Is there a way to generate the 8.3 or 'short' (Windows) version of a file name in Java?
You can get the short path and compare the generated path against both so you know which one to use.
I have some files inside a jar which I would like to access in Java using a File object rather than as a stream. Is it possible to do this?
Look at JarFile.
java.io.File is an abstraction from os specific handling of files. If you use java.io.File in your code, the code should run on all Java platforms.
The Jar is not a os file system. So it makes no sense to apply java.io.Files from the Java core classes.
I don't want to say it is not possible. Maybe it has sense for certain application and there is a library for that kind of abstraction.
You can also access it as a URL with a "jar:" prefix, but that's not a File object either, so I guess that doesn't meet the restriction.
Why do you have to access it as a File? This seems like asking, "Is there any way I can add two numbers without using the plus operator?" Maybe you can, but why do you not want to do it the easy way?
Supposing I have a File f that represents a directory, then f.delete() will only delete the directory if it is empty. I've found a couple of examples online that use File.listFiles() or File.list() to get all the files in the directory and then recursively traverses the directory structure and delete all the files. However, since it's possible to create infinitely recursive directory structures (in both Windows and Linux (with symbolic links)) presumably it's possible that programs written in this style might never terminate.
So, is there a better way to write such a program so that it doesn't fall into these pitfalls? Do I need to keep track of everywhere I've traversed and make sure I don't go around in circles or is there a nicer way?
Update: In response to some of the answers (thanks guys!) - I'd rather the code didn't follow symbolic links and stayed within the directory it was supposed to delete. Can I rely on the Commons-IO implementation to do that, even in the Windows case?
If you really want your recursive directory deletion to follow through symbolic links, then I don't think there is any platform independent way of doing so without keeping track of all the directories you have traversed.
However, in pretty much every case I can think of you would just want to delete the actual symbolic link pointing to the directory rather than recursively following through the symbolic link.
If this is the behaviour you want then you can use the FileUtils.deleteDirectory method in Apache Commons IO.
Try Apache Commons IO for a tested implementation.
However, I don't think it this handles the infinite-recursion problem.
File.getCanonicalPath() will tell you the “real” name of the file, including resolved symlinks. When while scanning you come across a directory you alread know (because you stored them in a Map) bail out.
If you could know which files are symlinks, you could just skip over those.
There is unfortunately no "clean" way of detecting symlinks in Java. Check out this pure Java workaround or this one involving native code.
At least under MacOSX, deleting a symbolic link to a directory does not delete the directory itself, and can therefore be deleted even if the target directory is not empty.
I assume this holds for most POSIX operating systems. And as far as I know, links under windows are also just files, and can be deleted as such from a Java program.