0

Im currently building an web app, using Java Servlets in Tomcat 7. This webapp uses Jupload as a client side applet to provide a more comfortable way of uploading multiple files to server. However, currently this applet sends the files one per post request. My implemented Servlet reads the data from input stream and stores it local. Thats fine and this works. But additional i have to store filename and paths and such things in DB. Thats why I wanted to store such informations in an object and keep them in a list, and collecting this infos during the incoming requests from the applet. The list is currently realized as class variable.

public class UploadServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    private ArrayList<ImageUploadInformation> uploadInfos;  //I know, thats bad.


     public UploadServlet() {
        super();
        uploadInfos = new ArrayList<>();
     }


    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // not relevant stuff...
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
         //accessing data stream directly
         //jUpload sends file information and file binary data in one stream, so we have to deal with mixed data in streams
         InputStream inputStream = request.getInputStream();
         DataInputStream dataInputStream = new DataInputStream(inputStream);

         //some other stuff, not relevant

         byte[] b = IOUtils.toByteArray(inputStream);
         File file = null; 
         if (finalFilename != null) {
             file = new File(finalFilename);
         }
         if (file != null) {
            FileOutputStream fos = new FileOutputStream(file);
            BufferedOutputStream bos = new BufferedOutputStream(fos);
            bos.write(b);
            bos.close();
            fos.close();
         }
         else 
            throw new IOException("File Creation on Server failed!");

         //adding Meta informations about file in list 
         uploadInfos.add(new ImageUploadInformation(filename, relativeDir,"1"));
    }
}

But i read on some threads here, that it is really a bad thing to do, in case of threadsafety. Im not very experinced in writing web applications, so maybe the following approach is completely wrong. I tried to bind the list as session attribute of the request.

 request.getSession().setAttribute("uploadInfos", uploadInfos);

However, I cannot use this, because it is a entirely new post request which comes from the applet, and that why I dont have access to this list, in another request. I read something about binding objects in ServletContext, but I think this is also a bad practice, but i couldnt find any proof for that. How can I achieve, that I can store this list over multiple independent requests. Would it be better, if all files will be sent to servlet in only one post request, where i can create the list inside of the doPost() Method ? Think this is configurable within Jupload, but actually the files could be very large. Is it common practice to send large amount of files in one request ? Thanks for any help and links to additional literature on that kind of stuff.

@edit: additional stuff tried also this..

        if (request.getSession().getAttribute("uploadInfos") != null) {     
            uploadInfos = (ArrayList<ImageUploadInformation>)request.getSession().getAttribute("uploadInfos");
            uploadInfos.add(new ImageUploadInformation(filename, relativeDir,"1"));
            System.out.println("UploadInfos found in Session, current size: " +uploadInfos.size());
            request.getSession().setAttribute("uploadInfos", uploadInfos);
        }
        else {
            System.out.println("No UploadInfos found, creating new List...");
            uploadInfos = new ArrayList<>();
            uploadInfos.add(new ImageUploadInformation(filename, relativeDir,"1"));
            request.getSession().setAttribute("uploadInfos", uploadInfos);
        }

Here's the output of the test:

Incoming post request
No UploadInfos found, creating new List...
Incoming post request
No UploadInfos found, creating new List...
Incoming post request
No UploadInfos found, creating new List...
lunatikz
  • 716
  • 1
  • 11
  • 27
  • 1
    You may need to consider using ServletContext, if you want to share data between sessions. – kosa Dec 13 '13 at 19:59
  • I've read this thread: http://stackoverflow.com/questions/3106452/how-do-servlets-work-instantiation-session-variables-and-multithreading/3106909#3106909 When I understand this right, my list would be available to all users, if they upload files concurrently. Or am i wrong ? – lunatikz Dec 13 '13 at 20:12
  • 1
    Yes, it is across users. That answer was written by one of the J2EE experts. – kosa Dec 13 '13 at 20:15
  • Are you trying to store information across all users? Or by user? – ᴇʟᴇvᴀтᴇ Dec 13 '13 at 20:17
  • I want to store them by user,.. not for all users. Servlet which is handling Client A should only have access to the list, where his file informations are stored and the same servlet which handles Client B requests should have another list, where his file informations is stored and have only access to it. – lunatikz Dec 13 '13 at 20:19
  • Then session state is definitely what you're after, as it does exactly this. It should work. Suggest you debug - e.g. look at the session returned each time, is it different? is it missing? (call `getSession(false)` at the start of the method and see if it's null) If not, what is its contents? – ᴇʟᴇvᴀтᴇ Dec 13 '13 at 22:47
  • getSession(false) returns null as expected.But the session ids differs from each request. hmm, why do i get -1, whats wrong with the question ? – lunatikz Dec 13 '13 at 23:43

1 Answers1

1

You're almost there. The session is where to store state, on the server, that you want to keep across requests.

Servlets may be called from multiple threads and from different clients; the container may also create or destroy instances as it pleases, so the servlets should not hold state themselves. The field "uploadInfos" needs to be removed. That list should be a thread-safe collection, e.g. CopyOnWriteArrayList, and stored in the session instead. First get the attribute from the session, if it's null, create a new list and store it in the session. Then add your entry to the list as before.

It's also worth mentioning that storing state between requests on the server is sometimes undesirable as it can make systems harder to scale out. An alternative would be to store the state on the client, using JavaScript. In your case, though, I wouldn't bother with that, just store it in a session. It's easier.

ᴇʟᴇvᴀтᴇ
  • 12,285
  • 4
  • 43
  • 66