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.
Related
If I have 2 Paths for files, both share the same FileStore, how can I verify with Java 17 whether they are pointing to the same file on disk (are hard-linked)? For Unix-like systems there seems at least to be a way to verify whether a file has other hardlinks (get Hard Link Count in Java), but on Windows/NTFS I haven't yet found a way to get either information, except of invoking fsutil hardlink list <file-path> and parsing the output. If necessary, a workaround using JNA would also fine for me.
PS: I have already searched Stackoverflow, but only found similar questions for Python or C#.
I doubt it can be done within Java alone.
Try to use getCanonicalPath() to at least get around relative and absolute path names. But this hardly gets you around symlinks, or symlinks in parent folders.
For such things in Linux systems there is the readlink command. There is lots to be found here on StackOverflow about it: https://stackoverflow.com/search?q=readlink
For Windows you already mentioned fsutil.
Files.isSameFile(path1, path2) checks whether they are hard-linked.
I recently started working on a POORLY designed and developed web application.. I am finding that it uses about 300 properties files, and all the properties files are being read somewhat like this:
Properties prop= new Properties();
FileInputStream fisSubsSysten = new FileInputStream("whatever.properties");
prop.load(fisSubsSysten);
That is, it is reading the properties files from current working directory.. Another problem is the developers have chosen to use the above lines multiple times within the same java file. For example if there are 10 methods, each method will have the above code instead of having one method and calling it wherever necessary..
This means, we can NEVER change the location of the properties files, currently they are directly under the websphere profiles directory, isn't this ugly? If I move them somewhere else, and set that location in classpath, it does not work.
I tried changing the above lines like this using Spring IO utils library:
Resource resource = new ClassPathResource("whatever.properties");
Properties prop = PropertiesLoaderUtils.loadProperties(resource);
But this application has over 1000 files, and I am finding it impossible to change each file.. How would you go about refactoring this mess? Is there any easy way around?
Thanks!
In these cases of "refactoring" i use a simple find and replace approach. Notepad++ has a " find in files" feature but there are plenty of similar programs.
Create a class which does the properties loading with a method probably with a name parameter for the property file.
This can be a java singleton or a spring bean.
Search and replace all "new Properties()" lines with an empty line.
Replace all "load..." lines with a reference to your new class/ method. Notepad++ supports regex replacement, so you can use the filename as a parameter.
Once this is done go to eclipse and launch a "cleanup" or "organize imports" and fix some compile errors manually if needed.
This approach is quite straight forward and takes no more than 10min if you are lucky or 1 hour if you are unlucky, f.e. the code formatting is way of and each file looks different.
You can make your replace simpler if you format the project once before with a line length of 300 or more so each java statement is on one line. This makes find and replace a bit easier as you dont have newlines to consider.
I can only agree that I find your project a bit daunting, from your reference.
However, the choice of how to maintain or improve of it is a risk that merely needs to be assessed and prioritised.
Consider building a highrise and subsequently realising the bolts that holds the infrastructure have a design flaw. The prospect of replacing them all is indeed daunting as well, so considerations into how to change them and if they really, really needs to be replaced, few, many or all.
I assume it must be a core system for the company, which somebody built and they have probably left the project (?), and you have consideration about improvement or maintaining them. But again, you must assess whether it really is important to move your property files, or if you can just for instance use symbolic links in your file system. Alternatively, do you really need to move them all or is there just a few that would really benefit from being moved. Can you just mark all places in the code with a marker to-be-fixed-later. I sometimes mark bad classes with deprecation, and promise to fix affected classes but postpone until I have other changes in those classes until finally the deprecated class can be safely removed.
Anyway you should assess your options, leave files, replace all or partials, and provide some estimation of cost and consequences, and ask your manager which course to take.
Just note that always overestimate the solution you don't want to do, as you would be twice as likely to stop for coffee breaks, and a billboard of told-you-so's is a great leverage for decision making :)
On the technology side of your question, regex search and replace is probably the only option. I would normally put configuration files in a place accessible by classpath.
You can try using eclipse search feature. For example if you right click on load() method of the properties class and select References -> Project it will give you all location in your project where that method is used.
Also from there maybe you can attempt a global regex search and replace.
I have a class that does operations on file's on a disk.
More exactly it traverses a directory, reads through all files with a given suffix
and does some operations on the data and then outputs them to a new file.
I'm a bit dubious as to how to design a unittest for this class.
I'm thinking having the setup method create a temporary directory and temporary files in /tmp/somefolder, but I'm suspecting this is a bad idea for a couple of reasons(Developers using windows, file permissions etc).
Another idea would be to mock the classes I'm using to write and read to the disk, by encapsulating the classes using an interface and then providing a mock object, but it seems to be a bit messy.
What would be the standard way of approaching such a problem?
If using JUnit 4.7 and up, you can use the #TemporaryFolder rule to transparently obtain a temporary folder which should automatically get cleared after each test.
Your strategy is the right one, IMO. Just make sure not to hardcode the temp directory. Use System.getProperty("java.io.tmpdir") to get the path of the temp directory, and use a finally block in your test or a #After method to cleanup the created files and directories once your test is finished.
Mocking everything out is possible, but probably much more effort than it's worth. You can use the temporary directory supplied from Java System.getProperty("java.io.tmpdir") which you should be able to write to etc. no matter which system you're on. Stick to short file names and you'll be safe even if running on something ancient.
If someone installs my java application on their computer (windows/linux/mac), how can I make sure my applications ability to read/write files works in all environments.
Is there a way for my application to figure out where in the file system it is installed, and then read/write safely?
Note: these files that are being read/written are all application files i.e. it is not trying to reference any operating system file/registry.
So all files can be within the applications root folder (wherever it is installed)
Using the java.io.File (or perhaps java.nio package). This will generally work cross-platform, but you will need to be aware of platform differences and code around these. For example, you need to use things like File.pathSeparator to ensure you use the correct path separator for the platform. Also, depending on what you are doing, there are differences between how locking works etc, and not all operations are guaranteed to work - some just fail silently.
Java's file IO library is platform independent and should work on any OS that the jvm is installed on. With that said, some file systems behave differently (permissions, user/group, etc), which can cause your file operations to succeed on one platform, but fail on another. For this reason, it is always a good idea to test your code on all systems you wish your system to run on.
You need to set a property, e.g., with "-Dapphome=abx" at the command line or in some configuration file. In the former you can retrieve it with System.getProperty("apphome"), in the latter you still need to have some way to find that configuration file unfortunately.
If it helps you can find the user's home directory with System.getProperty("user.home"). This can be very helpful since you can read per-user configuration files by using that as a starting point. Common files for multiple users will need to go into the system somewhere, e.g., /etc/appname/config.properties or C:\
BTW you should use System.getProperty("java.io.tmpdir") for your temporary files. Don't clutter up the directory where your app was launched (if you can -- you may not have the necessary permissions!) or the user's home directory. You need to be careful though - create a subdirectory for your app, and maybe a subdirectory for each user, to avoid the risk of one app stepping on the temporary files used by a second app.
The Java File class accepts "/" as path separators, so just use that. If you need the root drives do not code C: or anything but ask the JRE for the roots, and use them.
Is there a way for my application to figure out where in the file system it is installed, and then read/write safely?
You can maybe read the user.dir system property to get the path that the application was started in. Note that user.dir is a read only property, i.e. you can't change the "current directory" by setting the user.dir property. You read system properties with the System.getProperty(String) method. This is not exactly the same thing as "installed in" but it may work. But it's kinda weak.
If really you want the location of the install directory, either force the user to set an environment variable (MYAPP_HOME) or scan the whole file system. Personally, I don't like these options.
Actually, and if the data are user specific, the best choice in my opinion would be to read/write data in the user home directory (use the system property user.home to get it), for example in something like ~/.yourapp (Windows users never go in their %USER_HOME% anyway) or, even better, in a directory following Freedesktop XDG Base Directory Specification (obviously, only Linux users would care of that).
Then, to read/write, just use the java.io.File which is cross-platform when used properly (e.g. use File.separator if you need to build a path, don't use an hard coded version of the name-separator).
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.