4

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

5
  • 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? Commented Jul 26, 2013 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... Commented Jul 26, 2013 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... Commented Jul 26, 2013 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. Commented Jul 26, 2013 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 :) Commented Jul 26, 2013 at 12:59

4 Answers 4

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.

Sign up to request clarification or add additional context in comments.

6 Comments

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 :)
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: 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.
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...
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)?
|
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().

3 Comments

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.
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.
Hmmmm... I see, perhaps it was still open in the session, although I closed the FileOutputStream. I will try implementing the SessionListener :)
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.

5 Comments

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.
Are you using BufferedWriter for writing content to file ??? if yes then try to use FileOutputStream - Thanks
I was using BufferedWriter, then changed it to FileOutputStream :)
I would definitely avoid the deleteOnExit function in Tomcat. It will cause a memory leak and die. File.deleteOnExit memory leak information
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>");
    }
}

4 Comments

I have verified that the file is closed, even changed the way I write the file to reflect yours, but still nothing... :/
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.
@IMIordanov I have edited my answer and added a simple example based on the details you have adde to your question.
Nice, thank you very much! :D I just finished implementing my own SessionListener, but the results are not what I expected... See edit :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.