Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ObjectMapper fails to write to an OutputStream provided by a zip FileSystem... #680

Closed
fge opened this issue Jan 14, 2015 · 9 comments
Closed

Comments

@fge
Copy link
Contributor

fge commented Jan 14, 2015

Code:

    public static void main(final String... args)
        throws IOException
    {
        final Map<String, String> map
            = Collections.singletonMap("create", "true");
        final Path zipfile = Paths.get("/tmp/foo.zip");
        Files.deleteIfExists(zipfile);
        final URI uri = URI.create("jar:" + zipfile.toUri());
        final ObjectMapper mapper = new ObjectMapper();

        try (
            final FileSystem zipfs = FileSystems.newFileSystem(uri, map);
            final OutputStream out
                = Files.newOutputStream(zipfs.getPath("/t.json"));
        ) {
            mapper.writeValue(out, "hello");
        }
    }

Pretty standard way to write to a file in a zip, but unfortunately it fails:

fge@alustriel:~/src/perso/parboiled1/grappa$ unzip /tmp/foo.zip 
Archive:  /tmp/foo.zip
  inflating: t.json                  
  error:  invalid compressed data to inflate

That is very strange. I have no problems at all with any other OutputStream I can muster. Just these ones.

@cowtowncoder
Copy link
Member

Is the code above expected to produce a zip file? That is, if you try to write directly, would that produce a zip file? Instead of just regular one. Would URI pointing to a jar (to create) presumably create a zip file?

@fge
Copy link
Contributor Author

fge commented Jan 14, 2015

Yes, it will create the zip file ("create": "true") and a file named t.json at the root of it.

Well, that's what it should do... The result of this however is that since ObjectMapper fails for some reason, I have to go through a temporary file first (note: in the code below the ZIP filesystem is already created):

    @Override
    public void afterParse(final PostParseEvent<V> event)
    {
        final ParsingRunTrace trace = new ParsingRunTrace(startDate, events);
        final Path path = zipfs.getPath("/trace.json");
        final Path tmpfile;

        //noinspection OverlyBroadCatchBlock
        try {
            tmpfile = Files.createTempFile("trace", "xxx");
            //noinspection NestedTryStatement
            try (
                final OutputStream out = Files.newOutputStream(tmpfile);
            ) {
                MAPPER.writeValue(out, trace);
            }
            Files.move(tmpfile, path);
        } catch (IOException oops) {
            throw new RuntimeException("failed to write trace file", oops);
        }
    }

@fge
Copy link
Contributor Author

fge commented Jan 14, 2015

Well, I have opened this bug here but ultimately this is a bug with the ZIP filesystem provider. Note that both 1.8u25 and 1.7u72 are affected.

@cowtowncoder
Copy link
Member

One possible reason: if I recall correctly, zip classes have the odd feature where writers for individual entries should not be closed, as doing that will actually close the whole archive (it is very weird object hierarchy...)
So it may be that ObjectMappers closing of stream during processing causes issues. This can cause problems in some other cases, like JAX-RS serialization, where caller may want to keep OutputStream open.

So, if you can try disabling JsonGenerator.Feature.AUTO_CLOSE_TARGET that might resolve the problem.

@fge
Copy link
Contributor Author

fge commented Jan 14, 2015

Hmm, you're right, it did solve the problem.

Still, this remains a bug in the provider, it should not be able to create corrupt zip files except on errors. And when you close an output stream, well, it isn't supposed to close the whole zip...

@cowtowncoder
Copy link
Member

Right, I agree with you there. It really does not make much sense. :-(

@fge
Copy link
Contributor Author

fge commented Jan 14, 2015

The problem is that while I'd like to open a bug to OpenJDK, I can't manage to write an SSCCE...

How would you go about writing one? This code, for instance, where I close the output stream twice, doesn't trigger the bug...

        final Map<String, String> map
            = Collections.singletonMap("create", "true");
        final Path zipfile = Paths.get("/tmp/foo.zip");
        Files.deleteIfExists(zipfile);
        final URI uri = URI.create("jar:" + zipfile.toUri());

        final FileSystem zipfs = FileSystems.newFileSystem(uri, map);
        final OutputStream out
            = Files.newOutputStream(zipfs.getPath("/t.json"));

        try (
            final OutputStream otherOut = out;
        ) {
            otherOut.write(1);
        }
        out.close();
        zipfs.close();

@fge
Copy link
Contributor Author

fge commented Jan 14, 2015

Closing... I still cannot write an SSCCE so I asked on StackOverflow...

@fge fge closed this as completed Jan 14, 2015
@cowtowncoder
Copy link
Member

Ok. My experiences were with directly using classes from java.util.Zip. Looking at abstractions, this must have been with reading, so that closing InputStream gotten from ZipFile prevented iteration of remaining entries. So I don't know if this is directly related.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants