Skip to content

"zip file closed" error when building ClientBundles #10233

@niloc132

Description

@niloc132

This looks like a regression within 2.13.

On a specific machine, I've recently started noticing this error, both with Java 17 and 21 in the Hello and DynaTable samples:

     [java] GET /recompile/hello
     [java]    Job com.google.gwt.sample.hello.Hello_1_0
     [java]       starting job: com.google.gwt.sample.hello.Hello_1_0
     [java]       binding: user.agent=safari
     [java]       Compiling module com.google.gwt.sample.hello.Hello
     [java]          Computing all possible rebind results for 'com.google.gwt.user.client.ui.MenuBar.Resources'
     [java]             Rebinding com.google.gwt.user.client.ui.MenuBar.Resources
     [java]                Invoking generator com.google.gwt.resources.rebind.context.InlineClientBundleGenerator
     [java]                   [ERROR] Generator 'com.google.gwt.resources.rebind.context.InlineClientBundleGenerator' threw an exception while rebinding 'com.google.gwt.user.client.ui.MenuBar.Resources'
     [java] java.lang.IllegalStateException: zip file closed
     [java]     at java.base/java.util.zip.ZipFile.ensureOpen(ZipFile.java:846)
     [java]     at java.base/java.util.zip.ZipFile.getInputStream(ZipFile.java:375)
     [java]     at java.base/java.util.jar.JarFile.getInputStream(JarFile.java:856)
     [java]     at java.base/sun.net.www.protocol.jar.JarURLConnection.getContentType(JarURLConnection.java:221)
     [java]     at com.google.gwt.resources.rebind.context.AbstractResourceContext.deploy(AbstractResourceContext.java:80)
     [java]     at com.google.gwt.resources.rg.ImageResourceGenerator$ExternalImage.render(ImageResourceGenerator.java:333)
     [java]     at com.google.gwt.resources.rg.ImageResourceGenerator.renderImageMap(ImageResourceGenerator.java:709)
     [java]     at com.google.gwt.resources.rg.ImageResourceGenerator.createFields(ImageResourceGenerator.java:538)
     [java]     at com.google.gwt.resources.rebind.context.AbstractClientBundleGenerator.createFieldsAndAssignments(AbstractClientBundleGenerator.java:788)
     [java]     at com.google.gwt.resources.rebind.context.AbstractClientBundleGenerator.createFieldsAndAssignments(AbstractClientBundleGenerator.java:869)
     [java]     at com.google.gwt.resources.rebind.context.AbstractClientBundleGenerator.generateIncrementally(AbstractClientBundleGenerator.java:517)
     [java]     at com.google.gwt.dev.javac.StandardGeneratorContext.runGeneratorIncrementally(StandardGeneratorContext.java:739)
     [java]     at com.google.gwt.dev.cfg.RuleGenerateWith.realize(RuleGenerateWith.java:103)
     [java]     at com.google.gwt.dev.shell.StandardRebindOracle$Rebinder.rebind(StandardRebindOracle.java:78)
     [java]     at com.google.gwt.dev.shell.StandardRebindOracle.rebind(StandardRebindOracle.java:262)
     [java]     at com.google.gwt.dev.shell.StandardRebindOracle.rebind(StandardRebindOracle.java:251)
     [java]     at com.google.gwt.dev.PrecompilationContextCreator$1.getAllPossibleRebindAnswers(PrecompilationContextCreator.java:85)
     [java]     at com.google.gwt.dev.jjs.impl.UnifyAst$UnifyVisitor.createStaticRebindExpression(UnifyAst.java:525)
     [java]     at com.google.gwt.dev.jjs.impl.UnifyAst$UnifyVisitor.createRebindExpression(UnifyAst.java:493)
     [java]     at com.google.gwt.dev.jjs.impl.UnifyAst$UnifyVisitor.maybeHandleMagicMethodCall(UnifyAst.java:421)
     [java]     at com.google.gwt.dev.jjs.impl.UnifyAst$UnifyVisitor.visit(UnifyAst.java:408)
     [java]     at com.google.gwt.dev.jjs.ast.JMethodCall.traverse(JMethodCall.java:265)
     [java]     at com.google.gwt.dev.jjs.ast.JModVisitor.traverse(JModVisitor.java:361)
     [java]     at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:273)
     [java]     at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:265)
     [java]     at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:118)
     [java]     at com.google.gwt.dev.jjs.ast.JCastOperation.traverse(JCastOperation.java:76)
     [java]     at com.google.gwt.dev.jjs.ast.JModVisitor.traverse(JModVisitor.java:361)
     [java]     at com.google.gwt.dev.jjs.ast.JModVisitor.acceptImmutable(JModVisitor.java:305)
     [java]     at com.google.gwt.dev.jjs.ast.JMethodCall.visitChildren(JMethodCall.java:275)
     [java]     at com.google.gwt.dev.jjs.ast.JMethodCall.traverse(JMethodCall.java:266)
     [java]     at com.google.gwt.dev.jjs.ast.JModVisitor.traverse(JModVisitor.java:361)
     [java]     at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:273)
     [java]     at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:265)
     [java]     at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:118)
     [java]     at com.google.gwt.dev.jjs.ast.JExpressionStatement.traverse(JExpressionStatement.java:42)
     [java]     at com.google.gwt.dev.jjs.ast.JModVisitor$ListContext.traverse(JModVisitor.java:88)
     [java]     at com.google.gwt.dev.jjs.ast.JModVisitor.acceptWithInsertRemove(JModVisitor.java:331)
     [java]     at com.google.gwt.dev.jjs.ast.JBlock.traverse(JBlock.java:94)
     [java]     at com.google.gwt.dev.jjs.ast.JModVisitor.traverse(JModVisitor.java:361)
     [java]     at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:273)
     [java]     at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:139)
     [java]     at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:135)
     [java]     at com.google.gwt.dev.jjs.ast.JMethodBody.traverse(JMethodBody.java:83)
     [java]     at com.google.gwt.dev.jjs.ast.JModVisitor.traverse(JModVisitor.java:361)
     [java]     at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:273)
     [java]     at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:265)
     [java]     at com.google.gwt.dev.jjs.ast.JMethod.visitChildren(JMethod.java:786)
     [java]     at com.google.gwt.dev.jjs.ast.JConstructor.traverse(JConstructor.java:142)
     [java]     at com.google.gwt.dev.jjs.ast.JModVisitor.traverse(JModVisitor.java:361)
     [java]     at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:273)
     [java]     at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:265)
     [java]     at com.google.gwt.dev.jjs.impl.UnifyAst.mainLoop(UnifyAst.java:1407)
     [java]     at com.google.gwt.dev.jjs.impl.UnifyAst.exec(UnifyAst.java:902)
     [java]     at com.google.gwt.dev.jjs.JavaToJavaScriptCompiler.unifyJavaAst(JavaToJavaScriptCompiler.java:1410)
     [java]     at com.google.gwt.dev.jjs.JavaToJavaScriptCompiler.constructJavaAst(JavaToJavaScriptCompiler.java:1222)
     [java]     at com.google.gwt.dev.jjs.JavaToJavaScriptCompiler.precompile(JavaToJavaScriptCompiler.java:1137)
     [java]     at com.google.gwt.dev.jjs.JavaToJavaScriptCompiler.precompile(JavaToJavaScriptCompiler.java:261)
     [java]     at com.google.gwt.dev.Precompile.precompile(Precompile.java:247)
     [java]     at com.google.gwt.dev.Precompile.precompile(Precompile.java:194)
     [java]     at com.google.gwt.dev.Precompile.precompile(Precompile.java:135)
     [java]     at com.google.gwt.dev.Compiler.compile(Compiler.java:193)
     [java]     at com.google.gwt.dev.codeserver.Recompiler.doCompile(Recompiler.java:362)
     [java]     at com.google.gwt.dev.codeserver.Recompiler.compile(Recompiler.java:175)
     [java]     at com.google.gwt.dev.codeserver.Recompiler.recompile(Recompiler.java:134)
     [java]     at com.google.gwt.dev.codeserver.Outbox.recompile(Outbox.java:135)
     [java]     at com.google.gwt.dev.codeserver.JobRunner.recompile(JobRunner.java:113)
     [java]     at com.google.gwt.dev.codeserver.JobRunner$2.run(JobRunner.java:90)
     [java]     at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
     [java]     at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
     [java]     at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
     [java]     at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
     [java]     at java.base/java.lang.Thread.run(Thread.java:1583)
     [java]          [ERROR] Errors in 'com/google/gwt/user/client/ui/MenuBar.java'
     [java]             [ERROR] Line 321: Failed to resolve 'com.google.gwt.user.client.ui.MenuBar.Resources' via deferred binding
     [java]          [WARN] For the following type(s), generated source was never committed (did you forget to call commit()?)
     [java]             [WARN] com.google.gwt.user.client.ui.MenuBar_Resources_default_InlineClientBundleGenerator
     [java]          Computing all possible rebind results for 'com.google.gwt.user.client.ui.Tree.Resources'
     [java]             Rebinding com.google.gwt.user.client.ui.Tree.Resources
     [java]                Invoking generator com.google.gwt.resources.rebind.context.InlineClientBundleGenerator
     [java]                   [ERROR] Generator 'com.google.gwt.resources.rebind.context.InlineClientBundleGenerator' threw an exception while rebinding 'com.google.gwt.user.client.ui.Tree.Resources'
     [java] java.lang.IllegalStateException: zip file closed
     [java]     at java.base/java.util.zip.ZipFile.ensureOpen(ZipFile.java:846)
     [java]     at java.base/java.util.zip.ZipFile.getInputStream(ZipFile.java:375)
     [java]     at java.base/java.util.jar.JarFile.getInputStream(JarFile.java:856)
     [java]     at java.base/sun.net.www.protocol.jar.JarURLConnection.getContentType(JarURLConnection.java:221)
     [java]     at com.google.gwt.resources.rebind.context.AbstractResourceContext.deploy(AbstractResourceContext.java:80)
     [java]     at com.google.gwt.resources.rg.ImageResourceGenerator$ExternalImage.render(ImageResourceGenerator.java:333)
     [java]     at com.google.gwt.resources.rg.ImageResourceGenerator.renderImageMap(ImageResourceGenerator.java:709)
     [java]     at com.google.gwt.resources.rg.ImageResourceGenerator.createFields(ImageResourceGenerator.java:538)
     [java]     at com.google.gwt.resources.rebind.context.AbstractClientBundleGenerator.createFieldsAndAssignments(AbstractClientBundleGenerator.java:788)
     [java]     at com.google.gwt.resources.rebind.context.AbstractClientBundleGenerator.createFieldsAndAssignments(AbstractClientBundleGenerator.java:869)
     [java]     at com.google.gwt.resources.rebind.context.AbstractClientBundleGenerator.generateIncrementally(AbstractClientBundleGenerator.java:517)
     [java]     at com.google.gwt.dev.javac.StandardGeneratorContext.runGeneratorIncrementally(StandardGeneratorContext.java:739)
     [java]     at com.google.gwt.dev.cfg.RuleGenerateWith.realize(RuleGenerateWith.java:103)
     [java]     at com.google.gwt.dev.shell.StandardRebindOracle$Rebinder.rebind(StandardRebindOracle.java:78)
     [java]     at com.google.gwt.dev.shell.StandardRebindOracle.rebind(StandardRebindOracle.java:262)
     [java]     at com.google.gwt.dev.shell.StandardRebindOracle.rebind(StandardRebindOracle.java:251)
     [java]     at com.google.gwt.dev.PrecompilationContextCreator$1.getAllPossibleRebindAnswers(PrecompilationContextCreator.java:85)
     [java]     at com.google.gwt.dev.jjs.impl.UnifyAst$UnifyVisitor.createStaticRebindExpression(UnifyAst.java:525)
     [java]     at com.google.gwt.dev.jjs.impl.UnifyAst$UnifyVisitor.createRebindExpression(UnifyAst.java:493)
     [java]     at com.google.gwt.dev.jjs.impl.UnifyAst$UnifyVisitor.maybeHandleMagicMethodCall(UnifyAst.java:421)
     [java]     at com.google.gwt.dev.jjs.impl.UnifyAst$UnifyVisitor.visit(UnifyAst.java:408)
     [java]     at com.google.gwt.dev.jjs.ast.JMethodCall.traverse(JMethodCall.java:265)
     [java]     at com.google.gwt.dev.jjs.ast.JModVisitor.traverse(JModVisitor.java:361)
     [java]     at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:273)
     [java]     at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:265)
     [java]     at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:118)
     [java]     at com.google.gwt.dev.jjs.ast.JCastOperation.traverse(JCastOperation.java:76)
     [java]     at com.google.gwt.dev.jjs.ast.JModVisitor.traverse(JModVisitor.java:361)
     [java]     at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:273)
     [java]     at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:265)
     [java]     at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:118)
     [java]     at com.google.gwt.dev.jjs.ast.JDeclarationStatement.traverse(JDeclarationStatement.java:49)
     [java]     at com.google.gwt.dev.jjs.ast.JModVisitor$ListContext.traverse(JModVisitor.java:88)
     [java]     at com.google.gwt.dev.jjs.ast.JModVisitor.acceptWithInsertRemove(JModVisitor.java:331)
     [java]     at com.google.gwt.dev.jjs.ast.JBlock.traverse(JBlock.java:94)
     [java]     at com.google.gwt.dev.jjs.ast.JModVisitor.traverse(JModVisitor.java:361)
     [java]     at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:273)
     [java]     at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:139)
     [java]     at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:135)
     [java]     at com.google.gwt.dev.jjs.ast.JMethodBody.traverse(JMethodBody.java:83)
     [java]     at com.google.gwt.dev.jjs.ast.JModVisitor.traverse(JModVisitor.java:361)
     [java]     at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:273)
     [java]     at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:265)
     [java]     at com.google.gwt.dev.jjs.ast.JMethod.visitChildren(JMethod.java:786)
     [java]     at com.google.gwt.dev.jjs.ast.JMethod.traverse(JMethod.java:778)
     [java]     at com.google.gwt.dev.jjs.ast.JModVisitor.traverse(JModVisitor.java:361)
     [java]     at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:273)
     [java]     at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:265)
     [java]     at com.google.gwt.dev.jjs.impl.UnifyAst.mainLoop(UnifyAst.java:1407)
     [java]     at com.google.gwt.dev.jjs.impl.UnifyAst.exec(UnifyAst.java:902)
     [java]     at com.google.gwt.dev.jjs.JavaToJavaScriptCompiler.unifyJavaAst(JavaToJavaScriptCompiler.java:1410)
     [java]     at com.google.gwt.dev.jjs.JavaToJavaScriptCompiler.constructJavaAst(JavaToJavaScriptCompiler.java:1222)
     [java]     at com.google.gwt.dev.jjs.JavaToJavaScriptCompiler.precompile(JavaToJavaScriptCompiler.java:1137)
     [java]     at com.google.gwt.dev.jjs.JavaToJavaScriptCompiler.precompile(JavaToJavaScriptCompiler.java:261)
     [java]     at com.google.gwt.dev.Precompile.precompile(Precompile.java:247)
     [java]     at com.google.gwt.dev.Precompile.precompile(Precompile.java:194)
     [java]     at com.google.gwt.dev.Precompile.precompile(Precompile.java:135)
     [java]     at com.google.gwt.dev.Compiler.compile(Compiler.java:193)
     [java]     at com.google.gwt.dev.codeserver.Recompiler.doCompile(Recompiler.java:362)
     [java]     at com.google.gwt.dev.codeserver.Recompiler.compile(Recompiler.java:175)
     [java]     at com.google.gwt.dev.codeserver.Recompiler.recompile(Recompiler.java:134)
     [java]     at com.google.gwt.dev.codeserver.Outbox.recompile(Outbox.java:135)
     [java]     at com.google.gwt.dev.codeserver.JobRunner.recompile(JobRunner.java:113)
     [java]     at com.google.gwt.dev.codeserver.JobRunner$2.run(JobRunner.java:90)
     [java]     at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
     [java]     at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
     [java]     at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
     [java]     at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
     [java]     at java.base/java.lang.Thread.run(Thread.java:1583)
     [java]          [ERROR] Errors in 'com/google/gwt/user/client/ui/Tree.java'
     [java]             [ERROR] Line 133: Failed to resolve 'com.google.gwt.user.client.ui.Tree.Resources' via deferred binding
     [java]          [WARN] For the following type(s), generated source was never committed (did you forget to call commit()?)
     [java]             [WARN] com.google.gwt.user.client.ui.Tree_Resources_default_InlineClientBundleGenerator
     [java]          Unification traversed 20628 fields and methods and 1971 types. 1940 are considered part of the current module and 1940 had all of their fields and methods traversed.
     [java]       [ERROR] Compiler returned false
     [java]       [WARN] recompile failed
     [java]       [WARN] continuing to serve previous version

The code in question

try {
URLConnection urlConnection = resource.openConnection();
final byte[] bytes;
try (InputStream inputStream = urlConnection.getInputStream()) {
bytes = ByteStreams.toByteArray(inputStream);
}
String finalMimeType = (mimeType != null) ? mimeType : urlConnection.getContentType();

Related recent changes here are

  • 2b6f856 rewriting the Utils.readURLAsBytes to a local InputStream.readAllBytes() call. Noteworthy is that this then failed to close the input stream.
  • 90ec2a7 then fixed the leak, and rewrote away the readAllBytes() call to a guava call that compiles with --release 8.

The exception doesn't consistently happen, but it is hard for me to pin down at this time what the necessary conditions are.

https://stackoverflow.com/a/19125855/860630 seems to suggest that this could be caused by not setting useCaches=false on the URLConnection - which indeed the original Utils call did. However, just applying that change to the URLConnection did not resolve the issue, so I suspect it isn't the whole picture.

Looking more broading at the first commit above, the code in AbstractResourceContext.deploy() was also effectively opening a URLConnection twice, once to copy the file, and a second time to ask the file for its content type - which also requires reading the file via getInputStream, or at least part of it. My guess is that asking for a new input stream after closing the first (even though the streams must be independent, as they each read from the start) is related, so when the second patch correctly closed the first input stream, that introduced the bug. At a quick read I don't see how this is happening, as closing the InputStream doesn't appear to close the JarFile instance that the JarURLConnection is reading from.

Avoiding closing the first input stream until the content type has been read does appear to resolve the issue, which seems to confirm this hypothesis. Assuming this is the bug, I rechecked every other inlined case of Util.getURLAsBytes, but no other caller manipulates the URLConnection in quite this way.

One comes a bit close - note the javadoc:

/**
* Helper method to read the contentLength of a given URL, automatically
* closing the InputStream that is opened as a side effect.
*/
private int getContentLength(URL url) throws IOException {
URLConnection conn = url.openConnection();
try {
return conn.getContentLength();
} finally {
Closeables.closeQuietly(conn.getInputStream());
}
}

Indeed, the notes for URLConnection.connect() seems to confirm this
https://docs.oracle.com/javase/8/docs/api/java/net/URLConnection.html#connect--

Operations that depend on being connected, like getContentLength, will implicitly perform the connection, if necessary.

The getContentType method would seem to fall into that same category.

My conclusion is that the real "issue" was merging two distinct URLConnections to just one, but incorrectly closing between them. The remaining question: if this is a logic bug, why does it only sometimes fail (e.g. never in CI)?

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions