Copilot commented on code in PR #434:
URL: 
https://github.com/apache/jackrabbit-filevault/pull/434#discussion_r4123810520


##########
vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/JcrPackageImpl.java:
##########
@@ -334,8 +333,8 @@ protected VaultPackage getPackage(boolean forceFileArchive) 
throws RepositoryExc
                         bin.dispose();
                     }
                     pack = new ZipVaultPackage(tmpFile, true);
-                } catch (RepositoryException | IOException | RuntimeException 
e) {
-                    FileUtils.deleteQuietly(tmpFile);
+                } catch (IOException e) {
+                    tmpFile.delete();
                     throw e;
                 }

Review Comment:
   The new error path only deletes `tmpFile` for `IOException`, but failures 
here can also occur as `RepositoryException` (e.g., `getData()`, 
`bin.getStream()`) or `RuntimeException`, which will now leak the temp file 
(previously it was deleted in those cases). Also, `tmpFile.delete()` ignores 
its boolean result, so even the `IOException` path may silently fail to delete. 
Restore cleanup for all relevant exception types and perform deletion in a 
`finally` (or equivalent) using a best-effort delete that doesn’t silently fail 
(e.g., delete-if-exists / quiet delete), while still rethrowing the original 
exception.



##########
vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/ZipNioArchive.java:
##########
@@ -199,21 +207,48 @@ private static String getSystemId(Path zipPath, Path 
pathInZip) {
 
     @Override
     public void close() {
-        if (zipFileSystem != null) {
-            if (watcher != null) {
-                CloseWatcher.unregister(watcher);
-            }
+        if (watcher != null) {
             try {
-                zipFileSystem.close();
-            } catch (IOException e) {
-                log.warn("Error during close.", e);
+                watcher.getCloseable().close();
+            } catch (Exception e) {
+                // should not happen
             }
+            CloseWatcher.unregister(watcher);
+            zipFileSystem = null;
         }
-        if (deleteAtClose) {
-            try {
-                Files.delete(path);
-            } catch (IOException e) {
-                log.warn("Could not delete " + path, e);
+    }

Review Comment:
   `close()` unregisters the watcher but leaves `watcher` non-null, so repeated 
`close()` calls will repeatedly invoke the closer (and can trigger noisy warn 
logs when deleting a file twice). Make `close()` idempotent by nulling 
`watcher` after unregistering (and consider avoiding a blanket swallowed 
exception here—at least log at debug/warn if it occurs).



##########
vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/ZipArchive.java:
##########
@@ -219,19 +227,44 @@ public long getLastModified() {
 
     @Override
     public void close() {
-        try {
-            if (jar != null) {
-                jar.close();
-                jar = null;
-                if (watcher != null) {
-                    CloseWatcher.unregister(watcher);
-                }
+        if (watcher != null) {
+            try {
+                watcher.getCloseable().close();
+            } catch (Exception e) {
+                // should not happen
             }
-            if (file != null && isTempFile) {
-                FileUtils.deleteQuietly(file);
+            CloseWatcher.unregister(watcher);

Review Comment:
   Same pattern as `ZipNioArchive`: `watcher` is not set to null after 
unregistering, which makes `close()` non-idempotent and can lead to repeated 
cleanup and/or log noise on subsequent closes. Set `watcher = null` after 
unregistering to keep repeated `close()` calls safe and quiet.



##########
vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/JcrPackageImpl.java:
##########
@@ -334,8 +333,8 @@ protected VaultPackage getPackage(boolean forceFileArchive) 
throws RepositoryExc
                         bin.dispose();
                     }
                     pack = new ZipVaultPackage(tmpFile, true);
-                } catch (RepositoryException | IOException | RuntimeException 
e) {
-                    FileUtils.deleteQuietly(tmpFile);
+                } catch (IOException e) {
+                    tmpFile.delete();
                     throw e;

Review Comment:
   The prior regression test covering temp-file cleanup on 
`JcrPackageImpl#getPackage(true)` failure was removed, but this code path still 
has nuanced cleanup behavior and is easy to regress (especially with exception 
type changes). Add an equivalent regression test targeting 
`JcrPackageImpl#getPackage(...)` that simulates failures from both stream IO 
and JCR (`RepositoryException`), asserting the created temp file is removed.



##########
vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/ZipArchive.java:
##########
@@ -219,19 +227,44 @@ public long getLastModified() {
 
     @Override
     public void close() {
-        try {
-            if (jar != null) {
-                jar.close();
-                jar = null;
-                if (watcher != null) {
-                    CloseWatcher.unregister(watcher);
-                }
+        if (watcher != null) {
+            try {
+                watcher.getCloseable().close();
+            } catch (Exception e) {
+                // should not happen
             }
-            if (file != null && isTempFile) {
-                FileUtils.deleteQuietly(file);
+            CloseWatcher.unregister(watcher);
+            jar = null;
+        }
+    }
+
+    /**
+     * This class is used to close the zip file system and delete the zip file 
if requested.

Review Comment:
   This Javadoc is inaccurate for `ZipArchive`: it refers to closing a “zip 
file system”, but the implementation closes a `JarFile`. Update the comment to 
reflect the actual resource being managed (JarFile) to avoid confusion.



##########
vault-core/src/test/java/org/apache/jackrabbit/vault/fs/io/ZipNioArchiveTest.java:
##########
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.jackrabbit.vault.fs.io;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import static org.junit.Assert.*;
+
+/**
+ * Most tests in {@link ArchiveTest} are also applicable to {@link 
ZipNioArchive}.
+ * This class contains additional tests specific to {@link ZipNioArchive}.
+ */
+public class ZipNioArchiveTest {
+
+    @Rule
+    public TemporaryFolder tempFolder = new TemporaryFolder();
+
+    @Test
+    public void testDumpUnclosedArchivesClosesTmpFile() throws IOException, 
InterruptedException, URISyntaxException {
+        Path tmpFile = createTempPackage();
+        new ZipNioArchive(tmpFile, true);
+        System.gc();
+        Thread.sleep(100);
+        // Now dump unclosed archives
+        assertTrue("Couldn't find unclosed archives", 
AbstractArchive.dumpUnclosedArchives());
+        assertFalse("Temp file should have been deleted but still exists", 
Files.exists(tmpFile));

Review Comment:
   These tests rely on `System.gc()` + a fixed `Thread.sleep(100)` to make the 
archive eligible for leak detection, which is inherently flaky under CI and 
different JVMs. Prefer polling with a bounded timeout (retrying `System.gc()` 
and `dumpUnclosedArchives()` until success) or using a deterministic hook from 
the watcher/leak-detection mechanism (if available) so the test doesn’t depend 
on timing/GC behavior.



##########
vault-core/src/test/java/org/apache/jackrabbit/vault/fs/io/ZipNioArchiveTest.java:
##########
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.jackrabbit.vault.fs.io;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import static org.junit.Assert.*;
+
+/**
+ * Most tests in {@link ArchiveTest} are also applicable to {@link 
ZipNioArchive}.
+ * This class contains additional tests specific to {@link ZipNioArchive}.
+ */
+public class ZipNioArchiveTest {
+
+    @Rule
+    public TemporaryFolder tempFolder = new TemporaryFolder();
+
+    @Test
+    public void testDumpUnclosedArchivesClosesTmpFile() throws IOException, 
InterruptedException, URISyntaxException {
+        Path tmpFile = createTempPackage();
+        new ZipNioArchive(tmpFile, true);
+        System.gc();
+        Thread.sleep(100);
+        // Now dump unclosed archives
+        assertTrue("Couldn't find unclosed archives", 
AbstractArchive.dumpUnclosedArchives());
+        assertFalse("Temp file should have been deleted but still exists", 
Files.exists(tmpFile));
+    }
+
+    @Test
+    public void testDumpUnclosedArchivesClosesTmpFileAfterOpen()
+            throws IOException, InterruptedException, URISyntaxException {
+        Path tmpFile = createTempPackage();
+        new ZipNioArchive(tmpFile, true).open(true);
+        System.gc();
+        Thread.sleep(100);
+        // Now dump unclosed archives
+        assertTrue("Couldn't find unclosed archives", 
AbstractArchive.dumpUnclosedArchives());
+        assertFalse("Temp file should have been deleted but still exists", 
Files.exists(tmpFile));
+    }
+
+    private Path createTempPackage() throws URISyntaxException, IOException {
+        Path zipFile = Paths.get(ZipArchiveCloseTest.class

Review Comment:
   `ZipNioArchiveTest` loading resources via `ZipArchiveCloseTest.class` 
creates unnecessary coupling between test classes. Use 
`ZipNioArchiveTest.class.getResource(...)` (or a shared utility) to keep the 
test self-contained.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to