0

I am maintaining a Spring Boot project. There is this code:

BufferedReader reader = new BufferedReader(new FileReader("./setting_mail_sender.txt"));

Where should the file be located in this case?

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
pheromix
  • 18,213
  • 29
  • 88
  • 158
  • Does this answer your question? [What does a dot mean in a URL path?](https://stackoverflow.com/questions/6008829/what-does-a-dot-mean-in-a-url-path) – OH GOD SPIDERS Jul 19 '22 at 12:33
  • 1
    @OHGODSPIDERS I think they know what it means. They're asking what it means in the context of a spring boot application, which is not the same meaning it has when you run, e.g., a desktop application from the command line. – Federico klez Culloca Jul 19 '22 at 12:36
  • @pheromix you may want to clarify if that's what you mean – Federico klez Culloca Jul 19 '22 at 12:36
  • 1
    You need to put it in the "current working directory", see [How to get the current working directory in Java?](https://stackoverflow.com/questions/4871051/how-to-get-the-current-working-directory-in-java) how to find it out for an application. – Mark Rotteveel Jul 19 '22 at 12:39

1 Answers1

1

In 'the current working directory'. And where is that? Who knows!

Whomever wrote that code messed up. It's not a good idea to use the CWD for anything in any java code unless you specifically know you want it. And generally, that's only the case when you're writing command line tools in the vein of the various tools you find in your average linux distro's /bin dir - a rare occurrence, given that the JVM isn't really designed for that kind of thing.

There are 3 different best practices for 'data files' depending on the nature of the data:

  • Static, unchanging data - as much part of your app as your class files are. These should be loaded with MyClass.class.getResource("name-of-resource.txt") and shipped the same way your classes are. For example, inside the jar file.

  • Config files. These should usually be in System.getProperty("user.home") - the user's home dir; /Users/yourusername on macs, /home/yourusername on linux, C:\Users\YourUserName on windows. Best practice is to ship a 'template' version of the settings file if relevant in the jar file, and upon detecting that there is no config file present at all, to write out the template (and you load the template in via MyClass.class.getResource). If a template is not a good idea, something in a similar vein. Bad practice is to have the installer do this, and have your app be broken or leave the user having to peruse complex documentation to (re)create the config file. A different way to do it right is to have a config page in your app (a window, menu bar setting, web app thing - something with a user interface) where you can change settings and the config file is simply the way you store that data.

  • Changing data files. For example, you ship H2 (an all-java database engine) with your app and it needs to write its database file somewhere. This is a bit tricky; user home is not the right place for such data files, but you can't really 'find' the dir where your app is installed either. Even if you can, on non-badly-designed OSes, apps usually cannot (and should not!) be able to write to that location anyway. The location where this data is stored should definitely be configurable, so one easy way out is to require that the user explicitly picks a place. Otherwise I'm afraid you're stuck having to write per-OS code - find /Users/myusername/Library/Application Support/yourappname on mac, which is the right place. As far as I know there is no library to do this right.

None of them involve 'require that the user start the app with the right CWD'. There are good reasons for that: It can be hard to configure, and it's not something users think of to configure. For example, when setting up a java app as a recurring task in windows, you can configure the working dir for such a process, but it's not something that's usually considered as crucial configuration. When running a java app from the command line, who knows what the working dir is. You'll end up with an app that usually works, except in some circumstances when it magically doesn't, and most of your users have no idea that the difference between the magic run that works and the one that does not, is the directory they were in when they started the java app.

If you can edit that code, do so - figure out which of the 3 different kinds of data this is about (sounds like the second bullet: Config stuff, so should be in user home, and the app's name should be part of the file name) - and fix it. for example, that should be:

try (var in = Files.newBufferedReader(Paths.get(System.getProperty("user.home"), "myapp-mail.conf")) {

}

This solves a whole bunch of problems:

  • Uses try-with to avoid resource leakage.
  • Reads from user.home, avoiding current working directory as relevant setting.
  • Actually uses UTF-8 encoding (whereas your code will, at least until java 17, do 'platform default'. It's somewhat unlikely you want that, as it means your config file is not portable; copying it from one computer to another may break things. You presumably don't want this.
  • If errors occur, the error messages are improved (one of the downsides of the 'old' file API).

If you can't change this code, figure out what the CWD is; put the files there, and ensure that, however you start this spring boot project, you always start it from that directory. If you can't change this code but you can run some code in that JVM, you can print it: System.out.println(Paths.get(".").toAbsolutePath()) will show it to you.

rzwitserloot
  • 85,357
  • 5
  • 51
  • 72