dendini dendini - 2 months ago 12
reST (reStructuredText) Question

java.io.FileNotFoundException: /tmp/ (Too many open files)

I have a rest service in Java EE and for some weird backward compatibility reasons I have to return an .mdb file from a URL request after adding some rows inside it.

At first I simply opened an mdb file, cleared all rows in it, wrote my rows and returned it to the caller, however I realized the .mdb kept growing this way because Access doesn't purge the rows on deletion but only erases it and the library I am using (Jackcess) doesn't support purging completely the rows.

So I switched to creating a copy of an empty .mdb file with java.io.File.createTempFile() and returning it however a dangling pointer to the file in the /tmp/ folder is left and after several days I get a

java.io.FileNotFoundException: /tmp/tmpdb.mdb04949499 (Too many open files)


The only solutions I found so far are:


  1. Set MAX_FILE_HANDLES_FOR_READ_ENDS_MAP to a very high number (which only postpones the problem)

  2. deleting the temp file, which however is not viable because I return it from a function and once returned I lose control of the pointer.



below what I currently have:

GET
@Path("/get/database/{filename}")
@Produces("application/jet")
public StreamingOutput getDatabase(@PathParam("filename") String fileName)
{
//access files increase indefinitely in size because deleted rows are simply marked "deleted" and not removed
//so we create a temporary file equal to the template .mdb file and use it instead
java.io.File myDBFile = null;
try
{
java.io.File templateDBFile = new java.io.File(url + "resources/clean_tmpdb.mdb");
myDBFile = java.io.File.createTempFile("tmpdb", ".mdb");
myDBFile.deleteOnExit(); //useless hint
FileChannel src = new FileInputStream(templateDBFile).getChannel();
FileChannel dest = new FileOutputStream(myDBFile).getChannel();
dest.transferFrom(src, 0, src.size());
}
catch (IOException ex)
{
Logger.getLogger(FileResource.class.getName()).log(Level.SEVERE, null, ex);
}
finally
{
if (src != null)
{
try
{
src.close();
}
catch (IOException ex)
{
Logger.getLogger(FileResource.class.getName()).log(Level.SEVERE, null, ex);
}
}
if (dest != null)
{
try
{
dest.close();
}
catch (IOException ex)
{
Logger.getLogger(FileResource.class.getName()).log(Level.SEVERE, null, ex);
}
}

/* work on the file inserting rows */

return new FileStreamingOutput(myDBFile);
}


EDIT: found a similar question, with a vague accepted answer: How to delete file after REST response, the accepted answer is "just write directly to the output stream contained in the Response."

EJP EJP
Answer

You aren't closing either src or dest. So as to ensure they are closed, close them in finally blocks, or use the try-with-resources syntax.

return new FileStreamingOutput(myDBFile);
/* here I should call myDBFile.close(); and myDBFile.delete(); */

Here you can't call myDBFile.close(), as there is no such method. You can't call myDBFile.delete() either, as otherwise the caller will receive a File that doesn't exist. If the File needs to be deleted, the caller will have to do it. Your stated requirement doesn't make sense.