Java 11 handle unmodifiable map

202 views Asked by At

I have a class to process some files which is uploaded zipped. And a method to unzip and fill a HashMap and convert to an Collection.unmodifiableMap.

public class MyClass extends HttpServlet {

...
    private Map<String, String> rnaseqfiles = new HashMap<>();
...

    private void processZipFile(String zipfile) throws Exception {
        String fileName = zipfile;
        byte[] buffer = new byte[1024];
        try (ZipInputStream zis = new ZipInputStream(new FileInputStream(fileName))) {
            ZipEntry zipEntry = zis.getNextEntry();
            
            while (zipEntry != null) {
                File newFile = new File(diretorio, zipEntry.toString());
                if (zipEntry.isDirectory()) {
                    if (!newFile.isDirectory() && !newFile.mkdirs()) {
                        throw new IOException("Failed to create directory " + newFile);
                    }
                } else {
                    FileOutputStream fos = new FileOutputStream(newFile);
                    int len;
                    while ((len = zis.read(buffer)) > 0) {
                        fos.write(buffer, 0, len);
                    }
                    fos.close();
                    rnaseqfiles.put(zipEntry.toString(), newFile.getAbsolutePath());
                }
                zipEntry = zis.getNextEntry();
            }
            
            rnaseqfiles = Collections.unmodifiableMap(rnaseqfiles);
            
            zis.closeEntry();
            zis.close();
        }
    }
...
}

When I test with a small example, it works nicely, but when I try with the real case I got this kind of error.

java.lang.UnsupportedOperationException
        at java.base/java.util.Collections$UnmodifiableMap.put(Collections.java:1457)

I found some hints to deal with it but I don't know exactly what to do.

Any help is appreciated

1

There are 1 answers

0
rzwitserloot On BEST ANSWER

servlets are quite annoying. Think of the notion that any given servlet is likely going to run many times, and probably many times simultaneously, as various users hit your site.

They are the worst of both worlds: The servlet spec does not guarantee that the system initializes a new object for every request (meaning, it is possible that many different requests, some even simultaneous, are all using the same fields), but it als does not guarantee the opposite either: The system is free to do so.

Conclusion: Fields in servlets are pretty much useless. But you have one, and it's causing troubles: One 'run' overwrites your mutable hashmap with an immutable one, and then the next servlet tries to add stuff to this now immutable map.

The fix is generally to just get rid of servlets. There are better ways to write web apps these days, such as spark, DropWizard, Spring, and many others.

If you insist, then your servlets should not have any fields. If you desire them, your servlet code should simply make a new object and then invoke whatever you want there - your doGet and friends are mostly just oneliners of the form new ActualHandler(req, res).go() or similar. Now you actually have one instance per request.

Or, just.. write the code so that no fields are needed. I don't see why you need a field here, for example. Your current code does;

  • Receive the request and parse stuff out (you didn't paste this part)
  • That code evidently invokes processZipFile which returns nothing, but conveys data back using a field. (This does not work in servlets).
  • Your request handling code then uses that field for stuff.

Seems easy to replace that - don't have a field, have the processZipFile method return that map instead.