[java-identity-provider] 01/04: IDP-1682 Move the plugins webapp to a common location 1/many

Rod Widdowson rdw at steadingsoftware.com
Sun Oct 4 13:51:48 UTC 2020


This is an automated email from the git hooks/post-receive script.

rdw pushed a commit to branch main
in repository java-identity-provider.

View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=16460b1039c90a97b585ddeac5e02ba476a198f6

commit 16460b1039c90a97b585ddeac5e02ba476a198f6
Author: Rod Widdowson <rdw at steadingsoftware.com>
AuthorDate: Sat Oct 3 17:19:15 2020 +0100

    IDP-1682 Move the plugins webapp to a common location 1/many
    
    https://issues.shibboleth.net/jira/browse/IDP-1682
    
    Log the contents of what is added to the webapp folder to a property file.
---
 .../shibboleth/idp/installer/CopyDistribution.java |   2 +-
 .../idp/installer/plugin/impl/PluginInstaller.java | 135 ++++++------
 .../plugin/impl/PluginInstallerArguments.java      |   2 +-
 .../plugin/impl/PluginInstallerSupport.java        | 238 +++++++++++++++++++++
 .../idp/installer/plugin/impl/BasePluginTest.java  |   2 +-
 .../idp/installer/plugin/impl/PluginCLITest.java   |   2 +-
 6 files changed, 314 insertions(+), 67 deletions(-)

diff --git a/idp-installer/src/main/java/net/shibboleth/idp/installer/CopyDistribution.java b/idp-installer/src/main/java/net/shibboleth/idp/installer/CopyDistribution.java
index 049964139..34688209f 100644
--- a/idp-installer/src/main/java/net/shibboleth/idp/installer/CopyDistribution.java
+++ b/idp-installer/src/main/java/net/shibboleth/idp/installer/CopyDistribution.java
@@ -91,7 +91,7 @@ public final class CopyDistribution extends AbstractInitializableComponent {
      */
     protected void deleteOld() {
         delete(installerProps.getTargetDir().resolve("bin").resolve("lib"), null);
-        delete(installerProps.getTargetDir().resolve("dist"), "edit-webapp-*/**");
+        delete(installerProps.getTargetDir().resolve("dist"), "edit-webapp-*/** plugin-contents/**");
         delete(installerProps.getTargetDir().resolve("doc"), null);
         final Path system = installerProps.getTargetDir().resolve("system");
         if (Files.exists(system)) {
diff --git a/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginInstaller.java b/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginInstaller.java
index 1fca3c199..234955312 100644
--- a/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginInstaller.java
+++ b/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginInstaller.java
@@ -18,6 +18,7 @@
 package net.shibboleth.idp.installer.plugin.impl;
 
 import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
@@ -27,11 +28,9 @@ import java.io.OutputStream;
 import java.net.URL;
 import java.net.URLClassLoader;
 import java.nio.file.DirectoryStream;
-import java.nio.file.FileVisitResult;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.nio.file.SimpleFileVisitor;
-import java.nio.file.attribute.BasicFileAttributes;
+import java.time.Instant;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -40,8 +39,8 @@ import java.util.Optional;
 import java.util.Properties;
 import java.util.ServiceConfigurationError;
 import java.util.ServiceLoader;
-import java.util.Set;
 import java.util.ServiceLoader.Provider;
+import java.util.Set;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -57,7 +56,6 @@ import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
 import org.apache.commons.compress.utils.IOUtils;
 import org.apache.http.client.HttpClient;
 import org.apache.tools.ant.BuildException;
-import org.apache.tools.ant.taskdefs.Copy;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -71,6 +69,7 @@ import net.shibboleth.idp.installer.plugin.impl.TrustStore.Signature;
 import net.shibboleth.idp.module.IdPModule;
 import net.shibboleth.idp.module.ModuleContext;
 import net.shibboleth.idp.plugin.IdPPlugin;
+import net.shibboleth.idp.plugin.PluginVersion;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
 import net.shibboleth.utilities.java.support.collection.Pair;
@@ -123,6 +122,9 @@ public final class PluginInstaller extends AbstractInitializableComponent implem
 
     /** What to use to download things. */
     private HttpClient httpClient;
+ 
+    /** Files that were copied - to handle rollback. */
+    @Nonnull private List<Path> copiedFiles = new ArrayList<>();
 
     /** Set IdP Home.
      * @param home Where we are working from
@@ -167,22 +169,13 @@ public final class PluginInstaller extends AbstractInitializableComponent implem
         httpClient = Constraint.isNotNull(what, "HttpClient should be non-null");
     }
 
-    /** Return the canonical path.
-     * @param from the path we get given
-     * @return the canonicalized one
-     * @throws IOException  as from {@link File#getCanonicalFile()}
-     */
-    private static Path canonicalPath(final Path from) throws IOException {
-        return from.toFile().getCanonicalFile().toPath();
-    }
-
     /** Check that the provide path is inside {@link #idpHome}.
      * @param to the path to check.
      * @throws BuildException if it isn't
      */
     private void policeTo(final Path to) throws BuildException {
         try {
-            final Path canonicalTo = canonicalPath(to);
+            final Path canonicalTo = PluginInstallerSupport.canonicalPath(to);
             if (!canonicalTo.startsWith(idpHome)) {
                 LOG.error("File destination {} ({}) was illegal (not inside {}", to, canonicalTo, idpHome);
                 throw new BuildException("Illegal file destination");
@@ -199,7 +192,7 @@ public final class PluginInstaller extends AbstractInitializableComponent implem
      */
     private void policeFrom(final Path from) throws BuildException {
         try {
-            final Path canonicalFrom = canonicalPath(from);
+            final Path canonicalFrom = PluginInstallerSupport.canonicalPath(from);
             if (!canonicalFrom.startsWith(distribution)) {
                 LOG.error("File source {} ({}) was illegal (not inside {}", from, canonicalFrom, distribution);
                 throw new BuildException("Illegal file source");
@@ -252,12 +245,12 @@ public final class PluginInstaller extends AbstractInitializableComponent implem
         
         final Path myWebApp = idpHome.resolve("dist").resolve("edit-webapp-" + pluginId);
 
-        InstallerSupport.setReadOnly(myWebApp, false);
-        deleteTree(myWebApp);
+        uninstallOld(myWebApp);
         installWebapp(myWebApp);
         installFiles();
         downloadExternals();
         InstallerSupport.setReadOnly(myWebApp, true);
+        saveCopiedFiles();
 
         final BuildWar builder = new BuildWar(idpHome);
         try {
@@ -276,8 +269,7 @@ public final class PluginInstaller extends AbstractInitializableComponent implem
             LOG.error("Plugin {} had no jars installed.", pluginId);
             return;
         }
-        InstallerSupport.setReadOnly(myWebApp, false);
-        deleteTree(myWebApp);
+        uninstallOld(myWebApp);
         final BuildWar builder = new BuildWar(idpHome);
         try {
             builder.initialize();
@@ -457,9 +449,45 @@ public final class PluginInstaller extends AbstractInitializableComponent implem
      */
     private void installWebapp(final Path myWebApp) throws BuildException {
         final Path from = distribution.resolve("edit-webapp");
-        LOG.debug("Copying distribution from {} to {}", from, myWebApp);
-        final Copy copy = InstallerSupport.getCopyTask(from, myWebApp);
-        copy.execute();
+        if (PluginInstallerSupport.detectDuplicates(from, myWebApp)) {
+            throw new BuildException("Install would overwrite filess");
+        }
+        PluginInstallerSupport.copyWithLogging(from, myWebApp, copiedFiles);
+    }
+
+    /** Uninstall the old version of the plugin.
+     * @param myWebApp where to delete */
+    private void uninstallOld(final Path myWebApp) {
+        InstallerSupport.setReadOnly(myWebApp, false);
+        PluginInstallerSupport.deleteTree(myWebApp);
+    }
+
+    /** Stream the copy list to a property file and empty it.
+     * @throws BuildException If we hit an IO exception
+     */
+    private void saveCopiedFiles() throws BuildException {
+        try {
+            final Path parent = idpHome.resolve("dist").resolve("plugin-contents");
+            Files.createDirectories(parent);
+            final Properties props = new Properties(1+copiedFiles.size());
+            props.setProperty("idp.plugin.version",
+                    new PluginVersion(description.getMajorVersion(),
+                            description.getMinorVersion(),
+                            description.getPatchVersion()).toString());
+            int count = 1;
+            for (final Path p: copiedFiles) {
+                props.setProperty("idp.plugin.file."+Integer.toString(count++),
+                        PluginInstallerSupport.canonicalPath(p).toString());
+            }
+            final File outFile = parent.resolve(pluginId).toFile();
+            try (final BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(outFile))) {
+                props.store(out, "Files Copied "  + Instant.now());
+            }
+            copiedFiles = new ArrayList<>();
+        } catch (final IOException e) {
+            LOG.error("Error saving list of copied files.", e);
+            throw new BuildException(e);
+        }
     }
 
     /** Method to download a zip file to the {{@link #downloadDirectory}.
@@ -553,7 +581,7 @@ public final class PluginInstaller extends AbstractInitializableComponent implem
                     LOG.error("No contents unpacked from {}", fullName);
                     throw new BuildException("Distro was empty");
                 }
-                distribution = canonicalPath(contents.next());
+                distribution = PluginInstallerSupport.canonicalPath(contents.next());
                 if (contents.hasNext()) {
                     LOG.error("Too many packages in distributions {}", fullName);
                     throw new BuildException("Too many packages in distributions");
@@ -602,27 +630,27 @@ public final class PluginInstaller extends AbstractInitializableComponent implem
      * @throws BuildException if badness is detected.
      */
     private void setupPluginId() throws BuildException {
-        final File propertyFile = distribution.resolve("bootstrap").resolve("plugin.properties").toFile();
+        final File propertyFile = distribution.resolve("bootstrap").resolve("id.property").toFile();
         if (!propertyFile.exists()) {
             LOG.error("Could not locate identity of plugin. "
-                    + "Identity file 'bootstrap/plugin.properties' not present in plugin distribution");
+                    + "Identity file 'bootstrap/id.property' not present in plugin distribution.");
             throw new BuildException("Could not locate identity of plugin");
         }
         try (final InputStream inStream = new BufferedInputStream(new FileInputStream(propertyFile))) {
             final Properties idProperties = new Properties();
             idProperties.load(inStream);
-            final String id = StringSupport.trimOrNull(idProperties.getProperty("plugin.id"));
+            final String id = StringSupport.trimOrNull(idProperties.getProperty("pluginid"));
             if (id == null) {
-                LOG.error("Identity file 'bootstrap/plugin.properties' did not contain 'plugin.id' property");
-                throw new BuildException("No property in bootstrap/plugin.properties file");
+                LOG.error("Identity property file 'bootstrap/id.property' did not contain 'pluginid' property");
+                throw new BuildException("No property in ID file");
             }
             if (pluginId != null && !pluginId.equals(id)) {
-                LOG.error("Downloaded plugin id {} conflicts with provided id {}", id, pluginId);
+                LOG.error("Downloaded plugin id {} overriden by provided id {}", id, pluginId);
             } else {
                 setPluginId(id);
             }
         } catch (final IOException e) {
-            LOG.error("Could not load plugin identity file 'bootstrap/plugin.properties'", e);
+            LOG.error("Could not load plugin identity file 'bootstrap/id.property'", e);
             throw new BuildException(e);
         }
     }
@@ -678,7 +706,7 @@ public final class PluginInstaller extends AbstractInitializableComponent implem
             throw new ComponentInitializationException("Idp Home should be set");
         }
         try {
-            idpHome = canonicalPath(idpHome);
+            idpHome = PluginInstallerSupport.canonicalPath(idpHome);
         } catch (final IOException e) {
             LOG.error("Could not canonicalize idp home", e);
             throw new ComponentInitializationException(e);
@@ -715,39 +743,20 @@ public final class PluginInstaller extends AbstractInitializableComponent implem
         }
     }
     
-    /** Delete a directory tree. 
-     * @param directory what to delete
-     */
-    public static void deleteTree(@Nullable final Path directory) {
-        if (directory == null || !Files.exists(directory)) {
-            return;
-        }
-        LOG.debug("Deleting directory {}", directory);
-        try {
-            Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
-                @Override 
-                public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
-                    Files.delete(file);
-                    return FileVisitResult.CONTINUE;
-                }
-                @Override 
-                public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException {
-                    if (exc != null) {
-                        throw exc;
-                    }
-                    Files.delete(dir);
-                    return FileVisitResult.CONTINUE;
-                }
-            });
-        } catch (final IOException e) {
-            LOG.error("Couldn't delete {}", directory, e);
-        }
-    }
-    
     /** {@inheritDoc} */
     public void close() {
-        deleteTree(downloadDirectory);
-        deleteTree(unpackDirectory);
+        PluginInstallerSupport.deleteTree(downloadDirectory);
+        PluginInstallerSupport.deleteTree(unpackDirectory);
+        for (final Path p : copiedFiles) {
+            try {
+                if (Files.exists(p)) {
+                    Files.delete(p);
+                }
+            } catch (final IOException e) {
+                p.toFile().deleteOnExit();
+                LOG.warn("Failed to delete {}", p, e);
+            }
+        }
     }
     
 }
diff --git a/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginInstallerArguments.java b/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginInstallerArguments.java
index fe98a0fa8..2cfb6e8fc 100644
--- a/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginInstallerArguments.java
+++ b/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginInstallerArguments.java
@@ -272,7 +272,7 @@ public class PluginInstallerArguments extends AbstractIdPHomeAwareCommandLineArg
                 return OperationType.INSTALLREMOTE;
             }
         } catch (final MalformedURLException e) {
-            getLog().trace("urg");
+            // It's OK
         }
         // Must be a file
         final File inputAsFile = new File(input);
diff --git a/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginInstallerSupport.java b/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginInstallerSupport.java
new file mode 100644
index 000000000..fcbda89b0
--- /dev/null
+++ b/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/PluginInstallerSupport.java
@@ -0,0 +1,238 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.idp.installer.plugin.impl;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.FileVisitResult;
+import java.nio.file.FileVisitor;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.apache.tools.ant.BuildException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.utilities.java.support.annotation.constraint.Live;
+
+/**
+ * Support for copying files during plugin manipulation.
+ */
+public final class PluginInstallerSupport {
+    
+    /** Class logger. */
+    @Nonnull
+    private static final Logger LOG = LoggerFactory.getLogger(PluginInstallerSupport.class);
+
+    /** Constructor. */
+    private PluginInstallerSupport() {
+    }
+
+    /** Return the canonical path.
+     * @param from the path we get given
+     * @return the canonicalized one
+     * @throws IOException  as from {@link File#getCanonicalFile()}
+     */
+    static Path canonicalPath(final Path from) throws IOException {
+        return from.toFile().getCanonicalFile().toPath();
+    }
+
+    /** Delete a directory tree. 
+     * @param directory what to delete
+     */
+    public static void deleteTree(@Nullable final Path directory) {
+        if (directory == null || !Files.exists(directory)) {
+            return;
+        }
+        LOG.debug("Deleting directory {}", directory);
+        try {
+            Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
+                @Override 
+                public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
+                    Files.delete(file);
+                    return FileVisitResult.CONTINUE;
+                }
+                @Override 
+                public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException {
+                    if (exc != null) {
+                        throw exc;
+                    }
+                    Files.delete(dir);
+                    return FileVisitResult.CONTINUE;
+                }
+            });
+        } catch (final IOException e) {
+            LOG.error("Couldn't delete {}", directory, e);
+        }
+    }
+    
+    /** Traverse "from" looking to see if any of the files are already in "to".
+     * @param from source directory
+     * @param to target directory
+     * @return true if there was a match 
+     * @throws BuildException if anything threw and {@link IOException}
+     */
+    public static boolean detectDuplicates(final Path from, final Path to) throws BuildException {
+        
+        if (to == null || !Files.exists(to)) {
+            return false;
+        }
+        final NameClashVisitor detector = new NameClashVisitor(from, to);
+        LOG.debug("Walking {}, looking for a name clash in {}", from, to);
+        try {
+            Files.walkFileTree(from, detector);
+        } catch (final IOException e) {
+            LOG.error("Failed during duplicate detection:", e);
+            throw new BuildException(e);
+        }        
+        return detector.wasNameClash();
+    }
+    
+    /** Copy a directory tree and keep a log of what has changed.
+     * @param from source directory
+     * @param to target directory
+     * @param pathsCopied the list of files copied up (including if there was a failure)
+     * @throws BuildException from the copy
+     */
+    public static void copyWithLogging(final Path from, 
+            final Path to, @Live final List<Path> pathsCopied) throws BuildException {
+        if (from == null || !Files.exists(from)) {
+            return;
+        }
+        LOG.debug("Copying from {} to {}", from, to);
+        final LoggingVisitor visitor = new LoggingVisitor(from, to);
+        try {
+            Files.walkFileTree(from, visitor);
+        } catch (final IOException e) {
+            pathsCopied.addAll(visitor.getCopiedList());
+            LOG.error("Error copying files from {} to {}", from, to, e);
+            throw new BuildException(e);
+        }
+        pathsCopied.addAll(visitor.getCopiedList());
+    }
+    
+    /**
+     * A @{link {@link FileVisitor} which detects (and logs) whether a copy would overwrite.
+     */
+    private static final class NameClashVisitor extends SimpleFileVisitor<Path> {
+        /** did we find a duplicate. */
+        private boolean nameClash;
+
+        /** Path we are traversing. */
+        private final Path from;
+        
+        /** Path where we check for Duplicates. */
+        private final Path to;
+        /**
+         * Constructor.
+         *
+         * @param fromDir Path we are traversing
+         * @param toDir Path where we check for Duplicates
+         */
+        public NameClashVisitor(final Path fromDir, final Path toDir) {
+            from = fromDir;
+            to = toDir;
+        }
+        
+        @Override 
+        public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
+            final Path relFile = from.relativize(file);
+            final Path toFile = to.resolve(relFile);
+            if (Files.exists(toFile)) {
+                nameClash = true;
+                LOG.warn("{} already exists", toFile);
+            }
+            return FileVisitResult.CONTINUE;
+        }
+        
+        /** did we find a name clash?
+         * @return whether we found a name clash.
+         */
+        public boolean wasNameClash() {
+            return nameClash;
+        }
+    }
+    
+    /**
+     * A @{link {@link FileVisitor} which detects (and logs) whether a copy would overwrite.
+     */
+    private static final class LoggingVisitor extends SimpleFileVisitor<Path> {
+        /** How what files have we copied? */
+        private final List<Path> copiedFiles = new ArrayList<>();
+
+        /** Path we are traversing. */
+        private final Path from;
+        
+        /** Path where we check for Duplicates. */
+        private final Path to;
+        /**
+         * Constructor.
+         *
+         * @param fromDir Path we are traversing
+         * @param toDir Path where we check for Duplicates
+         */
+        public LoggingVisitor(final Path fromDir, final Path toDir) {
+            from = fromDir;
+            to = toDir;
+        }
+
+        @Override
+        public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs) throws IOException {
+            final Path relDir = from.relativize(dir);
+            final Path toDir = to.resolve(relDir);
+            if (!Files.exists(toDir)) {
+                LOG.trace("Creating directory {}", toDir);
+                Files.createDirectory(toDir);
+            }
+            return FileVisitResult.CONTINUE;
+        };
+
+        @Override 
+        public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
+            final Path relFile = from.relativize(file);
+            final Path toFile = to.resolve(relFile);
+            copiedFiles.add(toFile);
+            try(final InputStream in = new BufferedInputStream(new FileInputStream(file.toFile()));
+                final OutputStream out = new BufferedOutputStream(new FileOutputStream(toFile.toFile()))) {
+                in.transferTo(out);
+            }
+            return FileVisitResult.CONTINUE;
+        }
+        
+        /** did we find a name clash?
+         * @return whether we found a name clash.
+         */
+        public List<Path> getCopiedList() {
+            return copiedFiles;
+        }
+    }
+
+}
diff --git a/idp-installer/src/test/java/net/shibboleth/idp/installer/plugin/impl/BasePluginTest.java b/idp-installer/src/test/java/net/shibboleth/idp/installer/plugin/impl/BasePluginTest.java
index de117cd9c..e74af4040 100644
--- a/idp-installer/src/test/java/net/shibboleth/idp/installer/plugin/impl/BasePluginTest.java
+++ b/idp-installer/src/test/java/net/shibboleth/idp/installer/plugin/impl/BasePluginTest.java
@@ -71,7 +71,7 @@ public class BasePluginTest {
             return;
         }
         InstallerSupport.setReadOnly(idpHome, false);
-        PluginInstaller.deleteTree(idpHome);
+        PluginInstallerSupport.deleteTree(idpHome);
     }
 
     protected Path getIdpHome() {
diff --git a/idp-installer/src/test/java/net/shibboleth/idp/installer/plugin/impl/PluginCLITest.java b/idp-installer/src/test/java/net/shibboleth/idp/installer/plugin/impl/PluginCLITest.java
index 9e427c681..0ee11a716 100644
--- a/idp-installer/src/test/java/net/shibboleth/idp/installer/plugin/impl/PluginCLITest.java
+++ b/idp-installer/src/test/java/net/shibboleth/idp/installer/plugin/impl/PluginCLITest.java
@@ -111,7 +111,7 @@ public class PluginCLITest extends BasePluginTest {
                     AbstractCommandLine.RC_OK);
         } finally {
             if (unpack != null) {
-                PluginInstaller.deleteTree(unpack);
+                PluginInstallerSupport.deleteTree(unpack);
             }
         }
     }

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list