You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by kw...@apache.org on 2021/06/08 06:29:41 UTC

[jackrabbit-filevault] branch master updated: JCRVLT-531 always clean up hooks (#145)

This is an automated email from the ASF dual-hosted git repository.

kwin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/jackrabbit-filevault.git


The following commit(s) were added to refs/heads/master by this push:
     new 74d5b09  JCRVLT-531 always clean up hooks (#145)
74d5b09 is described below

commit 74d5b09b762e7b6e331aadf83e9428dd27507d8c
Author: Konrad Windszus <kw...@apache.org>
AuthorDate: Tue Jun 8 08:29:32 2021 +0200

    JCRVLT-531 always clean up hooks (#145)
---
 .../vault/packaging/InstallHookProcessor.java      | 10 ++-
 .../packaging/impl/InstallHookProcessorImpl.java   | 76 +++++++++++++---------
 .../vault/packaging/impl/ZipVaultPackage.java      | 61 ++++++++++-------
 .../jackrabbit/vault/packaging/package-info.java   |  2 +-
 4 files changed, 93 insertions(+), 56 deletions(-)

diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/InstallHookProcessor.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/InstallHookProcessor.java
index 3a0f6a4..1915442 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/InstallHookProcessor.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/InstallHookProcessor.java
@@ -16,6 +16,7 @@
  */
 package org.apache.jackrabbit.vault.packaging;
 
+import java.io.Closeable;
 import java.io.IOException;
 
 import org.apache.jackrabbit.vault.fs.api.VaultInputSource;
@@ -24,7 +25,7 @@ import org.apache.jackrabbit.vault.fs.io.Archive;
 /**
  * The install hook processor is used for handle the install hooks, from registration to execution.
  */
-public interface InstallHookProcessor {
+public interface InstallHookProcessor extends Closeable {
 
     /**
      * Register all hooks found in the given archive.
@@ -55,4 +56,11 @@ public interface InstallHookProcessor {
      * @return {@code true} if successful.
      */
     boolean execute(InstallContext context);
+
+    /**
+     * Cleans up any registered hooks
+     */
+    @Override
+    default void close() throws IOException {
+    }
 }
\ No newline at end of file
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/InstallHookProcessorImpl.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/InstallHookProcessorImpl.java
index 9137ec5..69e41c6 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/InstallHookProcessorImpl.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/InstallHookProcessorImpl.java
@@ -17,20 +17,20 @@
 
 package org.apache.jackrabbit.vault.packaging.impl;
 
-import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.OutputStream;
 import java.net.URL;
 import java.net.URLClassLoader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
 import java.util.Enumeration;
 import java.util.Properties;
 import java.util.TreeMap;
 import java.util.jar.JarFile;
 import java.util.jar.Manifest;
 
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.IOUtils;
+import org.apache.jackrabbit.util.Text;
 import org.apache.jackrabbit.vault.fs.api.VaultInputSource;
 import org.apache.jackrabbit.vault.fs.io.Archive;
 import org.apache.jackrabbit.vault.packaging.InstallContext;
@@ -39,7 +39,6 @@ import org.apache.jackrabbit.vault.packaging.InstallHookProcessor;
 import org.apache.jackrabbit.vault.packaging.PackageException;
 import org.apache.jackrabbit.vault.packaging.VaultPackage;
 import org.apache.jackrabbit.vault.util.Constants;
-import org.apache.jackrabbit.util.Text;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -53,7 +52,7 @@ public class InstallHookProcessorImpl implements InstallHookProcessor {
      */
     private static final Logger log = LoggerFactory.getLogger(InstallHookProcessorImpl.class);
 
-    private final TreeMap<String, Hook> hooks = new TreeMap<String, Hook>();
+    private final TreeMap<String, Hook> hooks = new TreeMap<>();
 
     public void registerHooks(Archive archive, ClassLoader classLoader) throws PackageException {
         try {
@@ -104,14 +103,17 @@ public class InstallHookProcessorImpl implements InstallHookProcessor {
 
     public void registerHook(VaultInputSource input, ClassLoader classLoader) throws IOException, PackageException {
         // first we need to spool the jar file to disk.
-        File jarFile = File.createTempFile("vaulthook", ".jar");
+        Path jarFile = Files.createTempFile("vaulthook", ".jar");
         Hook hook = new Hook(input.getSystemId(), jarFile, classLoader);
 
-        try (OutputStream out = FileUtils.openOutputStream(jarFile);
-             InputStream in = input.getByteStream()) {
-            IOUtils.copy(in, out);
+        try (InputStream in = input.getByteStream()) {
+            Files.copy(in, jarFile, StandardCopyOption.REPLACE_EXISTING);
         } catch (IOException e) {
-            hook.destroy();
+            try {
+                hook.destroy();
+            } catch (IOException ioeDuringDestroy) {
+                e.addSuppressed(ioeDuringDestroy);
+            }
             throw e;
         }
         initHook(hook);
@@ -120,13 +122,13 @@ public class InstallHookProcessorImpl implements InstallHookProcessor {
     private void initHook(Hook hook) throws IOException, PackageException {
         try {
             hook.init();
-        } catch (IOException e) {
-            log.error("Error while initializing hook: {}", e.toString());
-            hook.destroy();
-            throw e;
-        } catch (PackageException e) {
+        } catch (IOException|PackageException e) {
             log.error("Error while initializing hook: {}", e.toString());
-            hook.destroy();
+            try {
+                hook.destroy();
+            } catch (IOException ioeDuringDestroy) {
+                e.addSuppressed(ioeDuringDestroy);
+            }
             throw e;
         }
         hooks.put(hook.name, hook);
@@ -144,24 +146,38 @@ public class InstallHookProcessorImpl implements InstallHookProcessor {
             } catch (Throwable e) {
                 // abort processing only for prepare and installed phase
                 if (context.getPhase() == InstallContext.Phase.PREPARE || context.getPhase() == InstallContext.Phase.INSTALLED) {
-                    log.warn("Hook " + hook.name +" threw exception. {} aborted.", context.getPhase(), e);
+                    log.warn("Hook {} threw exception. {} aborted.", hook.name, context.getPhase(), e);
                     return false;
                 }
-                log.warn("Hook " + hook.name +" threw exception. Ignored", e);
+                log.warn("Hook {} threw exception. Ignored", hook.name, e);
             }
-            // if in end phase, shutdown hooks
-            if (context.getPhase() == InstallContext.Phase.END) {
+        }
+        return true;
+    }
+
+    @Override
+    public void close() throws IOException {
+        IOException ioException = null;
+        for (Hook hook : hooks.values()) {
+            try {
                 hook.destroy();
+            } catch (IOException e) {
+                if (ioException == null) {
+                    ioException = new IOException("Error while destroying one or more hooks. Look at suppressed exceptions for details!");
+                }
+                ioException.addSuppressed(e);
             }
         }
-        return true;
+        if (ioException != null) {
+            throw ioException;
+        }
     }
 
     private class Hook {
 
         private final String name;
 
-        private final File jarFile;
+        private final Path jarFile;
 
         private ClassLoader parentClassLoader;
 
@@ -176,17 +192,17 @@ public class InstallHookProcessorImpl implements InstallHookProcessor {
             this.jarFile = null;
         }
 
-        private Hook(String name, File jarFile, ClassLoader parentClassLoader) {
+        private Hook(String name, Path jarFile, ClassLoader parentClassLoader) {
             this.name = name;
             this.jarFile = jarFile;
             this.parentClassLoader = parentClassLoader;
         }
 
-        private void destroy() {
+        private void destroy() throws IOException {
             parentClassLoader = null;
             hook = null;
             if (jarFile != null) {
-                FileUtils.deleteQuietly(jarFile);
+                Files.deleteIfExists(jarFile);
             }
         }
 
@@ -194,7 +210,7 @@ public class InstallHookProcessorImpl implements InstallHookProcessor {
             try {
                 if (jarFile != null) {
                     // open jar file and extract classname from manifest
-                    try (JarFile jar = new JarFile(jarFile)) {
+                    try (JarFile jar = new JarFile(jarFile.toFile())) {
                         Manifest mf = jar.getManifest();
                         if (mf == null) {
                             throw new PackageException("hook jar file does not have a manifest: " + name);
@@ -209,17 +225,17 @@ public class InstallHookProcessorImpl implements InstallHookProcessor {
                         try {
                             // 1st fallback is the current classes classloader (the bundle classloader in the OSGi context)
                             loadMainClass(URLClassLoader.newInstance(
-                                    new URL[] { jarFile.toURI().toURL() },
+                                    new URL[] { jarFile.toUri().toURL() },
                                     this.getClass().getClassLoader()));
                         } catch (ClassNotFoundException cnfe) {
                             // 2nd fallback is the thread context classloader
                             loadMainClass(URLClassLoader.newInstance(
-                                    new URL[] { jarFile.toURI().toURL() },
+                                    new URL[] { jarFile.toUri().toURL() },
                                     Thread.currentThread().getContextClassLoader()));
                         }
                     } else {
                         loadMainClass(URLClassLoader.newInstance(
-                                new URL[] { jarFile.toURI().toURL() },
+                                new URL[] { jarFile.toUri().toURL() },
                                 parentClassLoader));
                     }
                 } else {
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/ZipVaultPackage.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/ZipVaultPackage.java
index d07dbdc..9e91635 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/ZipVaultPackage.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/ZipVaultPackage.java
@@ -192,33 +192,41 @@ public class ZipVaultPackage extends PackagePropertiesImpl implements VaultPacka
         InstallHookProcessor hooks = opts instanceof InstallHookProcessorFactory ?
                 ((InstallHookProcessorFactory) opts).createInstallHookProcessor()
                 : new InstallHookProcessorImpl();
-        if (!opts.isDryRun()) {
-            hooks.registerHooks(archive, opts.getHookClassLoader());
-        }
-
-        checkAllowanceToInstallPackage(session, hooks, securityConfig);
-
-        // check for disable intermediate saves (JCRVLT-520)
-        if (Boolean.parseBoolean(getProperty(PackageProperties.NAME_DISABLE_INTERMEDIATE_SAVE))) {
-            // MAX_VALUE disables saving completely, therefore we have to use a lower value!
-            opts.setAutoSaveThreshold(Integer.MAX_VALUE - 1);
-        }
-
-        Importer importer = new Importer(opts, isStrictByDefault);
-        AccessControlHandling ac = getACHandling();
-        if (opts.getAccessControlHandling() == null) {
-            opts.setAccessControlHandling(ac);
-        }
-        String cndPattern = getProperty(NAME_CND_PATTERN);
-        if (cndPattern != null) {
+        try {
+            if (!opts.isDryRun()) {
+                hooks.registerHooks(archive, opts.getHookClassLoader());
+            }
+            checkAllowanceToInstallPackage(session, hooks, securityConfig);
+    
+            // check for disable intermediate saves (JCRVLT-520)
+            if (Boolean.parseBoolean(getProperty(PackageProperties.NAME_DISABLE_INTERMEDIATE_SAVE))) {
+                // MAX_VALUE disables saving completely, therefore we have to use a lower value!
+                opts.setAutoSaveThreshold(Integer.MAX_VALUE - 1);
+            }
+    
+            Importer importer = new Importer(opts, isStrictByDefault);
+            AccessControlHandling ac = getACHandling();
+            if (opts.getAccessControlHandling() == null) {
+                opts.setAccessControlHandling(ac);
+            }
+            String cndPattern = getProperty(NAME_CND_PATTERN);
+            if (cndPattern != null) {
+                try {
+                    opts.setCndPattern(cndPattern);
+                } catch (PatternSyntaxException e) {
+                    throw new PackageException("Specified CND pattern not valid.", e);
+                }
+            }
+    
+            return new InstallContextImpl(session, "/", this, importer, hooks);
+        } catch (RepositoryException|PackageException e) {
             try {
-                opts.setCndPattern(cndPattern);
-            } catch (PatternSyntaxException e) {
-                throw new PackageException("Specified CND pattern not valid.", e);
+                hooks.close();
+            } catch (IOException exceptionDuringClose) {
+                e.addSuppressed(exceptionDuringClose);
             }
+            throw e;
         }
-
-        return new InstallContextImpl(session, "/", this, importer, hooks);
     }
 
     protected void checkAllowanceToInstallPackage(@NotNull Session session, @NotNull InstallHookProcessor hookProcessor, @NotNull AbstractPackageRegistry.SecurityConfig securityConfig) throws PackageException, RepositoryException {
@@ -279,6 +287,11 @@ public class ZipVaultPackage extends PackagePropertiesImpl implements VaultPacka
         } finally {
             ctx.setPhase(InstallContext.Phase.END);
             hooks.execute(ctx);
+            try {
+                hooks.close();
+            } catch (IOException e) {
+                log.warn("Error closing hook processor", e);
+            }
         }
         if (subPackages != null) {
             subPackages.addAll(importer.getSubPackages());
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/package-info.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/package-info.java
index 0c8c0f8..1d4a580 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/package-info.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/package-info.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-@Version("2.13.0")
+@Version("2.14.0")
 package org.apache.jackrabbit.vault.packaging;
 
 import org.osgi.annotation.versioning.Version;