[java-identity-provider] 03/03: IDP-1595 the work of installing a plugin
Rod Widdowson
rdw at steadingsoftware.com
Mon Jul 13 13:31:14 UTC 2020
This is an automated email from the git hooks/post-receive script.
rdw pushed a commit to branch master
in repository java-identity-provider.
View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=08f91a1c04dc8877a34b7dcb63f8e40a17307fa9
commit 08f91a1c04dc8877a34b7dcb63f8e40a17307fa9
Author: Rod Widdowson <rdw at steadingsoftware.com>
AuthorDate: Mon Jul 13 14:30:10 2020 +0100
IDP-1595 the work of installing a plugin
https://issues.shibboleth.net/jira/browse/IDP-1595
The code is linear and pretty self explanatory. tests are still
pending having a release plugin to play with.
---
.../idp/installer/plugin/impl/PluginInstaller.java | 170 +++++++++++++++++++--
.../installer/plugin/impl/PluginInstallerTest.java | 27 +++-
.../idphome-test/dist/webapp/WEB-INF/lib/.gitkeep | 0
idp-installer/src/test/resources/logback-test.xml | 1 +
4 files changed, 179 insertions(+), 19 deletions(-)
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 224aaac1c..abdcee379 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
@@ -51,15 +51,19 @@ 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;
import com.google.common.base.Predicates;
import net.shibboleth.ext.spring.resource.HTTPResource;
+import net.shibboleth.idp.installer.BuildWar;
+import net.shibboleth.idp.installer.InstallerSupport;
import net.shibboleth.idp.installer.plugin.impl.TrustStore.Signature;
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;
import net.shibboleth.utilities.java.support.component.AbstractInitializableComponent;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import net.shibboleth.utilities.java.support.httpclient.HttpClientBuilder;
@@ -88,6 +92,9 @@ public final class PluginInstaller extends AbstractInitializableComponent implem
/** Where we have downloaded. */
private Path downloadDirectory;
+ /** The plugin's story about itself. */
+ private PluginDescription description;
+
/** The callback before we install a certificate into the TrustStore. */
@Nonnull private Predicate<String> acceptCert = Predicates.alwaysFalse();
@@ -159,24 +166,149 @@ public final class PluginInstaller extends AbstractInitializableComponent implem
unpack(base, fileName);
setupPluginId();
checkSignature(base, fileName);
- //doInstall();
+ getDescription();
+
+ final Path myWebApp = idpHome.resolve("dist").resolve("edit-webapp-" + pluginId);
+ deleteTree(myWebApp);
+ installWebapp(myWebApp);
+ installFiles();
+ downloadExternals();
+
+ final BuildWar builder = new BuildWar(idpHome);
+ try {
+ builder.initialize();
+ } catch (final ComponentInitializationException e) {
+ throw new BuildException(e);
+ }
+ builder.execute();
}
- /** Method to download a zip file to the {{@link #downloadDirectory}.
- * @param baseURL Where the zip/tgz and signature file is
- * @param fileName the name.
+ /** Download any files that should not be shipped.
* @throws BuildException if badness is detected.
*/
- private void download(final URL baseURL, final String fileName) throws BuildException {
- if (httpClient == null) {
- log.debug("No HttpClient built, creating default builder");
+ private void downloadExternals() throws BuildException {
+ try {
+ for (final Pair<URL, Path> pair : description.getExternalFilePathsToCopy()) {
+ final Path to = idpHome.resolve(pair.getSecond());
+ if (Files.exists(to)) {
+ log.warn("{} exists, not copied", to);
+ continue;
+ }
+ buildHttpClient();
+ createParent(to);
+ log.debug("Copying from {} to {}", pair.getFirst(), to);
+ final Resource from = new HTTPResource(httpClient, pair.getFirst());
+ try (final InputStream in = new BufferedInputStream(from.getInputStream());
+ final OutputStream out = new BufferedOutputStream(new FileOutputStream(to.toFile()))) {
+
+ in.transferTo(out);
+
+ } catch (final IOException e) {
+ log.error("Could not copy from {} to {}", from, to, e);
+ throw new BuildException(e);
+ }
+ }
+ } catch (final IOException e) {
+ throw new BuildException(e);
+ }
+ }
+
+ /** Get hold of the {@link PluginDescription} for this plugin.
+ * @throws BuildException if badness is happens.
+ */
+ private void getDescription() throws BuildException {
+ final List<URL> urls = new ArrayList<>();
+ final Path libDir = distribution.resolve("edit-webapp").resolve("WEB-INF").resolve("lib");
+
+ try {
+ for (final Path jar : Files.newDirectoryStream(libDir)) {
+ urls.add(jar.toUri().toURL());
+ }
+ try (final URLClassLoader loader = new URLClassLoader(urls.toArray(URL[]::new))){
+
+ final ServiceLoader<PluginDescription> plugins = ServiceLoader.load(PluginDescription.class, loader);
+ for (final PluginDescription plugin:plugins) {
+ log.debug("Found Service announcing itself as {}", plugin.getPluginId() );
+ if (pluginId.equals(plugin.getPluginId())) {
+ description = plugin;
+ return;
+ }
+ }
+ }
+ log.error("Could not locate description for {} in distribution {}", pluginId, libDir);
+ throw new BuildException("Could not locate PluginDescription");
+ } catch (final IOException e) {
+ log.error("Could not get description of {} from {}", pluginId, libDir, e);
+ throw new BuildException(e);
+ }
+ }
+
+ /** Copy the files the distribution tells us to.
+ * @throws BuildException if badness is happens.
+ */
+ private void installFiles() throws BuildException {
+ for (final Path p : description.getFilePathsToCopy()) {
+ final Path from = distribution.resolve(p);
+ final Path to = idpHome.resolve(p);
+ if (Files.exists(to)) {
+ log.debug("File {} exists, skipping", to);
+ continue;
+ }
+ if (!Files.exists(from)) {
+ log.warn("Source File {} does not exists, skipping", from);
+ continue;
+ }
try {
- httpClient = new HttpClientBuilder().buildClient();
- } catch (final Exception e) {
- log.error("Could not create HttpClient", e);
+ createParent(to);
+ log.debug("Copying from {} to {}", from, to);
+ try (final InputStream in = new BufferedInputStream(new FileInputStream(from.toFile()));
+ final OutputStream out = new BufferedOutputStream(new FileOutputStream(to.toFile()))) {
+ in.transferTo(out);
+ }
+ } catch (final IOException e) {
+ log.error("Could not copy from {} to {}", from, to, e);
throw new BuildException(e);
}
}
+ }
+
+ /** If the parent dir of the provided path doesn't exist, create it.
+ * @param file where the file will go
+ * @throws IOException if the directory couldn't be created
+ * @throws BuildException if the parent wasnt a directory
+ */
+ private void createParent(final Path file) throws IOException, BuildException {
+ final Path parent = file.resolve("..");
+ if (!Files.exists(parent)) {
+ log.debug("Creating parent directory {}", parent);
+ Files.createDirectories(parent);
+ } else if (!Files.isDirectory(parent)) {
+ log.error("{} exists and is not a directory", parent);
+ throw new BuildException("Parent of target file was not a directory");
+ } else {
+ log.trace("Parent directory {} existed", parent);
+ }
+ }
+
+ /** Copy the webapp folder from the distribution to the per plugin
+ * location inside dist.
+ * @param myWebApp Where to put it.
+ * @throws BuildException if badness is detected.
+ */
+ 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();
+ }
+
+ /** Method to download a zip file to the {{@link #downloadDirectory}.
+ * @param baseURL Where the zip/tgz and signature file is
+ * @param fileName the name.
+ * @throws BuildException if badness is detected.
+ */
+ private void download(final URL baseURL, final String fileName) throws BuildException {
+ buildHttpClient();
try {
downloadDirectory = Files.createTempDirectory("plugin-installer-download");
final Resource baseResource = new HTTPResource(httpClient, baseURL);
@@ -188,6 +320,19 @@ public final class PluginInstaller extends AbstractInitializableComponent implem
}
}
+ /** Build the Http Client if it doesn't exist. */
+ private void buildHttpClient() {
+ if (httpClient == null) {
+ log.debug("No HttpClient built, creating default");
+ try {
+ httpClient = new HttpClientBuilder().buildClient();
+ } catch (final Exception e) {
+ log.error("Could not create HttpClient", e);
+ throw new BuildException(e);
+ }
+ }
+ }
+
/** Download helper method.
* @param baseResource where to go for the file
* @param fileName the file name
@@ -256,7 +401,7 @@ public final class PluginInstaller extends AbstractInitializableComponent implem
throw new BuildException(e);
}
}
- // CheckStyle: CyclomaticComplexity OFF
+ // CheckStyle: CyclomaticComplexity ON
/** does the file name end in .zip?
* @param fileName the name to consider
@@ -390,9 +535,10 @@ public final class PluginInstaller extends AbstractInitializableComponent implem
* @param directory what to delete
*/
private void deleteTree(@Nullable final Path directory) {
- if (directory == null) {
+ if (directory == null || !Files.exists(directory)) {
return;
}
+ log.debug("Deleting directory {}", directory);
try {
Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
@Override
diff --git a/idp-installer/src/test/java/net/shibboleth/idp/installer/plugin/impl/PluginInstallerTest.java b/idp-installer/src/test/java/net/shibboleth/idp/installer/plugin/impl/PluginInstallerTest.java
index b26c97c76..ff15e8b9b 100644
--- a/idp-installer/src/test/java/net/shibboleth/idp/installer/plugin/impl/PluginInstallerTest.java
+++ b/idp-installer/src/test/java/net/shibboleth/idp/installer/plugin/impl/PluginInstallerTest.java
@@ -24,14 +24,15 @@ import java.io.IOException;
import java.net.URL;
import java.security.Security;
import java.util.List;
+import java.util.function.Predicate;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
-import com.google.common.base.Predicates;
-
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import net.shibboleth.utilities.java.support.plugin.AbstractPluginDescription;
import net.shibboleth.utilities.java.support.plugin.PluginDescription;
@@ -39,13 +40,15 @@ import net.shibboleth.utilities.java.support.plugin.PluginDescription;
@SuppressWarnings("javadoc")
public class PluginInstallerTest {
+ private final Logger log = LoggerFactory.getLogger(PluginInstallerTest.class);
+
@BeforeClass public void setup() throws IOException {
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
Security.addProvider(new BouncyCastleProvider());
}
}
- @Test public void testListing() throws ComponentInitializationException, IOException {
+ @Test(enabled = false) public void testListing() throws ComponentInitializationException, IOException {
try (final PluginInstaller inst = new PluginInstaller()) {
inst.setIdpHome(new ClassPathResource("idphome-test").getFile().toPath());
@@ -58,16 +61,17 @@ public class PluginInstallerTest {
@Test(enabled = false) public void testUnpackZip() throws ComponentInitializationException, IOException {
try (final PluginInstaller inst = new PluginInstaller()) {
inst.setIdpHome(new ClassPathResource("idphome-test").getFile().toPath());
+ inst.setAcceptCert(new LoggingAcceptor());
inst.initialize();
- final File f = new File("H:\\Perforce\\Juno\\New\\plugins\\java-idp-plugin-scripting\\nashorn-dist\\target");
- inst.installPlugin(f.toPath(),"shibboleth-idp-plugin-nashorn-0.0.1-SNAPSHOT.zip");
+ final File f = new File("H:\\Perforce\\Juno\\New\\plugins\\java-idp-plugin-scripting\\rhino-dist\\target");
+ inst.installPlugin(f.toPath(),"shibboleth-idp-plugin-rhino-0.0.1-SNAPSHOT.zip");
}
}
@Test(enabled = false) public void testUnpackTgz() throws ComponentInitializationException, IOException {
try (final PluginInstaller inst = new PluginInstaller()) {
inst.setIdpHome(new ClassPathResource("idphome-test").getFile().toPath());
- inst.setAcceptCert(Predicates.alwaysTrue());
+ inst.setAcceptCert(new LoggingAcceptor());
inst.initialize();
final File f = new File("H:\\Perforce\\Juno\\New\\plugins\\java-idp-plugin-scripting\\nashorn-dist\\target");
inst.installPlugin(f.toPath(),"shibboleth-idp-plugin-nashorn-0.0.1-SNAPSHOT.tar.gz");
@@ -77,7 +81,7 @@ public class PluginInstallerTest {
@Test(enabled = false) public void testDownload() throws ComponentInitializationException, IOException {
try (final PluginInstaller inst = new PluginInstaller()) {
inst.setIdpHome(new ClassPathResource("idphome-test").getFile().toPath());
- inst.setAcceptCert(Predicates.alwaysTrue());
+ inst.setAcceptCert(new LoggingAcceptor());
inst.initialize();
final URL url = new URL("http://iis.steadingsoftware.net/plugins/");
inst.installPlugin(url,"shibboleth-idp-plugin-nashorn-0.0.1-SNAPSHOT.tar.gz");
@@ -108,4 +112,13 @@ public class PluginInstallerTest {
}
}
+
+ public class LoggingAcceptor implements Predicate<String> {
+
+ /** {@inheritDoc} */
+ public boolean test(String what) {
+ log.debug("Accepting the cetrtificate {}", what);
+ return true;
+ }
+ }
}
diff --git a/idp-installer/src/test/resources/idphome-test/dist/webapp/WEB-INF/lib/.gitkeep b/idp-installer/src/test/resources/idphome-test/dist/webapp/WEB-INF/lib/.gitkeep
new file mode 100644
index 000000000..e69de29bb
diff --git a/idp-installer/src/test/resources/logback-test.xml b/idp-installer/src/test/resources/logback-test.xml
index d3b8a58c9..042bb588c 100644
--- a/idp-installer/src/test/resources/logback-test.xml
+++ b/idp-installer/src/test/resources/logback-test.xml
@@ -3,6 +3,7 @@
<configuration>
<logger name="org" level="INFO"/>
+ <logger name="net.shibboleth.idp.installer" level="TRACE"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list