1

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."

Community
  • 1
  • 1
dendini
  • 3,842
  • 9
  • 37
  • 74
  • 3
    Make sure you are closing all the streams when you are done with them – MadProgrammer Apr 21 '15 at 09:41
  • 1
    You don't close your resources; no wonder you get ENFILE. Also, this is 2015 so use java.nio.file and try-with-resources. – fge Apr 21 '15 at 09:43
  • 1
    How? look at the code, I need to return the file from the function which is called internally when a user requests the REST url www.example.com:8080/myapp/webresources/get/database/aaa.mdb – dendini Apr 21 '15 at 09:43
  • 2
    Returning the `File` doesn't prevent you from closing the `FileChannels.` – user207421 Apr 21 '15 at 10:01
  • I updated the question showing how Filechannels are now closed. The too many open files however seems to be related to the temporary files created. deleteOnExit() doesn't solve the problem, the problem still is unresolved for me. – dendini Oct 06 '16 at 08:26

1 Answers1

1

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.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • Hmm ok it's a mistake in the SSCE, but the problem is with the myDBFile which I don't know how to close before returning, not with src or dest Filechannels. – dendini Apr 21 '15 at 10:04
  • @dendini `myDBFile` is a File. You can't close it. It's irrelevant. The problem with the code you posted is as I have stated. If that isn't the real code, post the real code. – user207421 Apr 21 '15 at 10:06
  • Hi I updated the answer showing now src and dest are correctly closed, the problem is still there though. The requirement I have makes sense: I have to return a modified-on-the-fly file which is stored in the server, I thus create a temp file which I modify and then return it to the client, the temp file however somehow remains open and causes "too many open files". – dendini Oct 06 '16 at 08:29
  • Ok found out there was another opened database locking the temp file, fixed it now and as far as the code above is concerned, your answer solves it. – dendini Oct 06 '16 at 11:21