Javalin: Possible problem with JavalinVue when using Location.EXTERNAL - not closing files?

Created on 10 Sep 2019  路  5Comments  路  Source: tipsy/javalin

Actual behavior/steps to reproduce (the bug)

  1. Set external location
JavalinVue.rootDirectory("/vue", Location.EXTERNAL)
  1. (have a large number of .vue files in /vue folder)

  2. Run something that produces a lot of requests, e.g. scenario tests

  3. Produces java.nio.file.FileSystemException: [...]/src/main/resources/vue: Too many open files exception

https://gist.github.com/jorunfa/4278f4d0c231d7b54d3348464689d94d

I'll try to investigate the issue myself.

BUG

Most helpful comment

In the last days I saw something in the code base.

fun Path.readText(): String {
    val s = Scanner(Files.newInputStream(this), "UTF-8").useDelimiter("\\A")
    return if (s.hasNext()) s.next() else ""
}

The scanner is never closed, could this be the problem? What about Files.readAllBytes()?

All 5 comments

It seems to happen if I run the scenario tests sequentially too, not just when running them in parallell, it just takes longer time. So maybe either files are not being closed as they should be, or the amount of files just makes it so that requests are opening files faster than it's able to close them.

In the last days I saw something in the code base.

fun Path.readText(): String {
    val s = Scanner(Files.newInputStream(this), "UTF-8").useDelimiter("\\A")
    return if (s.hasNext()) s.next() else ""
}

The scanner is never closed, could this be the problem? What about Files.readAllBytes()?

I think you're completely right.

fun Path.readText(): String = Files.readAllBytes(this).toString(charset("UTF-8"))

Seems to have done the trick.

Could also add .also { s.close() } to the current code:

fun Path.readText(): String {
    val s = Scanner(Files.newInputStream(this), "UTF-8").useDelimiter("\\A")
    return (if (s.hasNext()) s.next() else "").also { s.close() }
}

I'm not really against using Files.readAllBytes(this).toString(charset("UTF-8")), I'm just wondering if it will behave the same in all the different environments, so closing the scanner seems safer.

readAllBytes seems to work fine in distroless Java container (which was where we had a problem before).

I have very little experience with the Scanner class, so I'm not sure, but it seems like one of its primary purposes is scanning for input, i.e. from user input through System.in, but of course other sources are also possible. E.g.:

Scanner sc = new Scanner(System.in);
int i = sc.nextInt(); 

https://docs.oracle.com/javase/7/docs/api/java/util/Scanner.html

We always read the full .vue file. readAllBytes makes sense to me. Not my area of expertise though.

Was this page helpful?
0 / 5 - 0 ratings