3

I am working on a Java Servlet which creates a temporary file to use in a session. At the end of the session (ie, user "logout"), I need to remove the temporary file, and then redirect the user to the initial ("login") page. The redirection works fine, but the temporary file remains intact.

I presume it has something to do with the path to the file, but I am not quite sure as to what. I create the file when the session initiates:

String path = request.getSession().getServletContext().getRealPath("/");
File file = File.createTempFile("getter", ".html", new File(path + "/tmp/"));

Then, when closing the session, I do:

file.delete();

I know about file.deleteOnExit(), but... when do I exit a servlet? Perhaps I am confused, but I would surely appreciate any help! :)

Thank you in advance!

EDIT

So, here come some details:

I am using a servlet, as I said, for the time being without handling sessions. I agree with @Joop that I will need to implement sessions, but for the time being just wanted to do some simple testing.

So, my servlet hagles GET and POST requests. I use a flag in the POST request to call an internal function which instantiates the file (declared in the class as private File file;) to a new temp file. On consecutive calls, the file gets populated and saved. In the page the user sees, I have an anchor referring to the servlet (to 'this', that is), passing a flag as a parameter, a flag that indicates the 'logout'. Then I call another internal function which deletes the file previously instantiated.

If it is a matter of sesions, I will implement the manager and post my findings.

EDIT 2

I implemented an HttpSessionListener, and all seems to work fine. Now, on creating the session, I instantiate a file in my previously declared directory (note that it is not a temp file, I use File file = new File(path + "/tmp/" + req.getSession().getId() + ".html"); so the name of the file equals the session ID). Then I add an attribute to the session, whose value is the full path to the file. I proceed to populate my file as always, and when the user selects to log out, I invalidate the session. Then, inside the listener, I retrieve the path to the file, hence I can acquire the pointer to it:

String fname = ev.getSession().getAttribute("filename").toString();
File f = new File(fname);
f.delete();

So, now the messages I am getting are positive, I mean f.delete() returns true, and after this I do f.exists() and I get false. So it should be OK. However, the files physically exist, that is they are still present on the disk.

I can try the example so kindly provided by @A4L. Have I done something wrong..?

Iordan Iordanov
  • 253
  • 1
  • 3
  • 13
  • Where do you see the file is still there? Do you use a file esxplorer or do you see it in your project inside an IDE (for example in eclipse you have to refresh you project then the file will disappear from the view). Are you sure the file is not a zombie file from previous runs where problably the deletion was not working yet? – A4L Jul 26 '13 at 11:47
  • Yes, I forgot to mention this. As I said in another comment, I have the full name of the file output in various places. So, when creating the file, I get: `Created file: F:\workspace\eclipse\.metadata\.plugins\org.eclipse.wst.server.core\tmp1\wtpwebapps\JavaTestServlet\tmp\241B5CA17233009D92AAA46E1E378BAE.html` Then, after deleting the file, I check whether `file.exists();`, and I get: `F:\workspace\eclipse\.metadata\.plugins\org.eclipse.wst.server.core\tmp1\wtpwebapps\JavaTestServlet\tmp\241B5CA17233009D92AAA46E1E378BAE.html exists: false` But the file can be seen from a file explorer... – Iordan Iordanov Jul 26 '13 at 12:08
  • I tried adding the check for the existence of the file *before* the deletion. So, I get that the file exists, then it's successfully deleted, and then it doesn't exist anymore. Which is OK. Only that the file still resides on the file system... – Iordan Iordanov Jul 26 '13 at 12:17
  • This is strange :S ... From what I see you have set your project as `Dynamic web project` in eclipse. In this case eclipse manages your web-content directory witch resides somewhere in the `.metadata` directory of your workspace. Maybe eclipse is doing something weird there. You should try to manually deploy your project into a standalone tomcat (without eclipse) and see if the file does still exist after deletion. – A4L Jul 26 '13 at 12:23
  • As I commented on the post of @BalusC, I may be able to eliminate temporary files. In the last try, I wasn't even able to **create** the temp file, so I think working without them would be even better. Thank you very much for your advice, it has been invaluable :) – Iordan Iordanov Jul 26 '13 at 12:59

4 Answers4

9

Please stop writing arbitrary files to deploy folder during runtime. Just write to a real temp folder instead. Get rid of the following line altogether:

path = request.getSession().getServletContext().getRealPath("/");

and just use

File file = File.createTempFile("getter", ".html");

Your concrete problem is likely caused because the files in the deploy folder are usually locked by the servletcontainer. You can't delete files therein.

A hint for the future: whenever you occur to think that the getRealPath() could solve a problem, you should immediately stop writing code and think twice about whether it's the right tool to fix the concrete problem. In the decade I developed Servlet based web applications, there was no one sensible real world use case for that method. See also What does servletcontext.getRealPath("/") mean and when should I use it


I know about file.deleteOnExit(), but... when do I exit a servlet?

You don't. The container does. The "exit" here basically means the shutdown of the whole JVM. This is even literally mentioned in the javadoc (emphasis mine).

Requests that the file or directory denoted by this abstract pathname be deleted when the virtual machine terminates.

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Thank you for your insight :) It's always nice to have someone experienced give you a hint. I think I will be able to completely eradicate the use of temporary files, perhaps it will even work better. I will have a look at the articles you included for future reference :) – Iordan Iordanov Jul 26 '13 at 12:57
  • It would probably be a good idea to create the temp file in the webapp's own temporary directory (`(File)servletContext.getAttribute(ServletContext.TEMPDIR)`) – Ian Roberts Jul 26 '13 at 12:58
  • @Ian: the `File#createTempFile()` uses `java.io.tmpdir` which is already properly preset to exactly that value by the container. So no need to go clumsy. – BalusC Jul 26 '13 at 13:02
  • I intuitively believe BalusC is correct. I tried using your suggestion, but since I need to serve the page created, I have no permission (the file gets stored in the **local** temp directory). So, perhaps I haven't configured Eclipse correctly and the application doesn't get deployed correctly, therefore it's not accessing the Tomcat temp directory, but the machine's. @A4L has hinted upon this as well, so I think I do have to take a better look at my settings... – Iordan Iordanov Jul 26 '13 at 14:20
  • `java.io.tmpdir` is global per-JVM, I thought the servlet context attribute gave you a different directory for each webapp (e.g. under `$CATALINA_HOME/work` on Tomcat)? – Ian Roberts Jul 26 '13 at 14:57
  • Generally speaking, generating temporary content to a file on the disk, then having the container serve it, then deleting it is a recipe for disaster: the container will attempt to cache the file, etc., chaos will ensue. – Christopher Schultz Jul 26 '13 at 16:12
0

Make sure you have closed the file before attempting to delete it when the user logs out and check what File#delete() returns.

@Test
public void createTempFile() throws IOException {
    File tf = File.createTempFile("hello", ".tmp", new File("."));
    FileOutputStream fos = new FileOutputStream(tf);
    fos.write("Hello, Temp!".getBytes());
    Assert.assertTrue(tf.delete()); // fails because the file could not be deleted
                                    // and delete() returns false
}

vs.

@Test
public void createTempFile() throws IOException {
    File tf = File.createTempFile("hello", ".tmp", new File("."));
    FileOutputStream fos = new FileOutputStream(tf);
    fos.write("Hello, Temp!".getBytes());
    fos.close();
    Assert.assertTrue(tf.delete()); // passes, file deleted
}

With File#deleteOnExit() the file will be deleted twhen the VM exits, this happen when your tomcat is shutdown. So it won't help on user logout.

EDIT

Make sure you have only one file per user and across mutiple requests. I suggest you use a SessionListener as Joop suggested, create the file when HttpSessionListener#sessionCreated is called and put it into the session with a well known key, you can get the session object using HttpSessionEvent#getSession(). When you logout call HttpSession.#invalidate(), the Listner method HttpSessionListener#sessionDestroyed will be called, then you can get the file from the session and delete it.

Simple example, (doGet only and without SessionListener)

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@WebServlet(urlPatterns = "/ptf.html")
public class PopulateTempFile extends HttpServlet { 
    private static final long serialVersionUID = -144949663400032218L;

    private static class TempFilePopulator {
        private File tf = null;
        public TempFilePopulator(String rootDir) throws IOException {
            tf = File.createTempFile("hello", ".tmp", new File(rootDir));
        }

        public void populate(String line) throws IOException {
            FileWriter fw = new FileWriter(tf, true);
            fw.write(line + "\n");
            fw.close();
        }

        public List<String> getContent() throws IOException {
            List<String> lines = new ArrayList<String>();
            BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(tf)));
            String line;
            while(null != (line = br.readLine())) {
                lines.add(line);
            }
            br.close();
            return lines;
        }

        public boolean deleteTempFile() { return tf.delete(); }
        public String toString() { return tf.getAbsolutePath(); }
    }


    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {

        HttpSession session = request.getSession();
        TempFilePopulator tfp = (TempFilePopulator) session.getAttribute("tempfilepopulator");

        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("<html>");

        out.println("<a href=\"" + request.getServletContext().getContextPath()
            + request.getServletPath() + "\">Refresh</a>");
        out.println("&nbsp;|&nbsp;");
        out.println("<a href=\"" + request.getServletContext().getContextPath()
            + request.getServletPath() + "?logout=true\">Logout</a>");

        String logout = request.getParameter("logout");
        if("true".equals(logout)) {
            if(tfp != null) {
                if(tfp.deleteTempFile()) {
                    log("Temp file '" + tfp + "' deleted.");
                } else {
                    log("Unable to delete temp file '" + tfp + "'");
                }
            }
            session.invalidate();
        } else {
            if(tfp == null) {
                tfp = new TempFilePopulator(request.getServletContext().getRealPath("/"));
                log("Temp file '" + tfp + "' created.");
                session.setAttribute("tempfilepopulator", tfp);
            }
            tfp.populate(new Date().toString());
            out.println("<p>Content of temp file</p>");
            List<String> lines = tfp.getContent();
            for(String line : lines) {
                out.write(line);
                out.write("<br/>");
            }
        }
        out.println("</html>");
    }
}
A4L
  • 17,353
  • 6
  • 49
  • 70
  • I have verified that the file is closed, even changed the way I write the file to reflect yours, but still nothing... :/ – Iordan Iordanov Jul 26 '13 at 09:28
  • I am currently in the phase of testing, therefore I am the only user using the system. I agree that I must implement a session listener, as @Joop suggests. I wanted to try it as simple as possible for starters, hence the missing session listener. I will edit my initial post to add details. – Iordan Iordanov Jul 26 '13 at 09:44
  • @IMIordanov I have edited my answer and added a simple example based on the details you have adde to your question. – A4L Jul 26 '13 at 10:46
  • Nice, thank you very much! :D I just finished implementing my own SessionListener, but the results are not what I expected... See edit :) – Iordan Iordanov Jul 26 '13 at 10:56
0

Every call to createTempFile gives another path, so the path has to be stored.

See SessionListener. Example - if session timeout is involved.

Maybe use the JSESSIONID as directory with temp files and delete the directory.

BTW I presume you invalidate the session after file.delete() as otherwise getSession() would create a new session. I would log file.getPath().

Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
  • I print messages on the console during the creation/deletion, so here is what I get: `Created file: F:\workspace\eclipse\.metadata\.plugins\org.eclipse.wst.server.core\tmp1\wtpwebapps\JavaTestServlet\tmp\getter423480123629841021.html` Then, when deleting it, I get the following: `Deleting file: F:\workspace\eclipse\.metadata\.plugins\org.eclipse.wst.server.core\tmp1\wtpwebapps\JavaTestServlet\tmp\getter423480123629841021.html --> FAILURE` Note that the last part of the second message (`--> FAILURE`) is my own, indicating that `file.exists()` is `true` after the deletion. – Iordan Iordanov Jul 26 '13 at 09:32
  • Then @A4L seems right, and the file was not closed, in use. That could also happen by a virus scanner or automatic backup, but unlikely. – Joop Eggen Jul 26 '13 at 09:51
  • Hmmmm... I see, perhaps it was still open in the session, although I closed the `FileOutputStream`. I will try implementing the `SessionListener` :) – Iordan Iordanov Jul 26 '13 at 09:58
0

You can also use the deleteOnExit() method ... please have a look at the java doc of the createTempFile() -

Creates a new empty file in the specified directory, using the given prefix and 
suffix strings to generate its name. If this method returns successfully then 
it is guaranteed that:

The file denoted by the returned abstract pathname did not exist before this 
method was invoked and , 

Neither this method nor any of its variants will return the same abstract 
pathname again in the current invocation of the virtual machine. 

This method provides only part of a temporary-file facility. To arrange 
for a file created by this method to be deleted automatically, use the
deleteOnExit() method.
saurav
  • 3,424
  • 1
  • 22
  • 33
  • Yes, but as I said in my initial post, the file will be deleted `onExit`, that is when Tomcat closes. I don't want this, I want to manually delete the file while Tomcat is running. – Iordan Iordanov Jul 26 '13 at 09:40
  • Are you using BufferedWriter for writing content to file ??? if yes then try to use FileOutputStream - Thanks – saurav Jul 26 '13 at 09:54
  • For more you can refer this question - http://stackoverflow.com/questions/991489/i-cant-delete-a-file-in-java – saurav Jul 26 '13 at 09:57
  • I was using `BufferedWriter`, then changed it to `FileOutputStream` :) – Iordan Iordanov Jul 26 '13 at 09:59
  • I would definitely avoid the deleteOnExit function in Tomcat. It will cause a memory leak and die. [File.deleteOnExit memory leak information](http://nineofclouds.blogspot.com/2013/08/pdf-service-memory-leaks.html) – mbarlocker Aug 18 '13 at 00:16