[java-identity-provider] 02/02: IDP-1711 Add a simple program to look for duplicate jars/classes
Rod Widdowson
rdw at steadingsoftware.com
Wed Nov 18 16:33:55 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=08f7f026034af21eeed1a858963d38cb1938a287
commit 08f7f026034af21eeed1a858963d38cb1938a287
Author: Rod Widdowson <rdw at steadingsoftware.com>
AuthorDate: Wed Nov 18 16:33:11 2020 +0000
IDP-1711 Add a simple program to look for duplicate jars/classes
https://issues.shibboleth.net/jira/browse/IDP-1711
---
idp-admin-impl/pom.xml | 8 +-
.../idp/admin/impl/JarCheckArguments.java | 83 +++++++
.../net/shibboleth/idp/admin/impl/JarCheckCLI.java | 274 +++++++++++++++++++++
idp-installer/pom.xml | 1 -
4 files changed, 364 insertions(+), 2 deletions(-)
diff --git a/idp-admin-impl/pom.xml b/idp-admin-impl/pom.xml
index 162baa7e2..ed3ab5ab6 100644
--- a/idp-admin-impl/pom.xml
+++ b/idp-admin-impl/pom.xml
@@ -130,9 +130,15 @@
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
- <scope>provided</scope>
</dependency>
+ <dependency>
+ <!-- Only used in a command line -->
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-compress</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
<!-- Runtime Dependencies -->
<!--
These are strictly correct due to the embedded flows,
diff --git a/idp-admin-impl/src/main/java/net/shibboleth/idp/admin/impl/JarCheckArguments.java b/idp-admin-impl/src/main/java/net/shibboleth/idp/admin/impl/JarCheckArguments.java
new file mode 100644
index 000000000..01a3c01e0
--- /dev/null
+++ b/idp-admin-impl/src/main/java/net/shibboleth/idp/admin/impl/JarCheckArguments.java
@@ -0,0 +1,83 @@
+/*
+ * 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.admin.impl;
+
+import java.io.PrintStream;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.beust.jcommander.Parameter;
+
+import net.shibboleth.idp.cli.AbstractIdPHomeAwareCommandLineArguments;
+
+/**
+ * Command line arguments for {@link JarCheckCLI}.
+ */
+public class JarCheckArguments extends AbstractIdPHomeAwareCommandLineArguments {
+
+ /** Logger. */
+ @Nullable private Logger log;
+
+ /** look inside the jars. */
+ @Parameter(names= {"-d", "--detailed"})
+ @Nullable private boolean detailed;
+
+ /** provide a sorted list. */
+ @Parameter(names= {"-l", "--list"})
+ @Nullable private boolean list;
+
+ /** {@inheritDoc} */
+ @Nonnull public Logger getLog() {
+ if (log == null) {
+ log = LoggerFactory.getLogger(JarCheckArguments.class);
+ }
+ return log;
+ }
+
+ /** Are we doing an in depth survey?.
+ * @return Returns detailed.
+ */
+ public boolean isDetailed() {
+ return detailed;
+ }
+
+ /** are we listing everything?
+ * @return Returns list.
+ */
+ public boolean isList() {
+ return list;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void printHelp(final PrintStream out) {
+ out.println("JarCheck");
+ out.println("Provides a command line interface to look for duplicates in an installation");
+ out.println();
+ out.println(" XXXX [options] ");
+ out.println();
+ super.printHelp(out);
+ out.println();
+ out.println(String.format(" %-22s %s", "-l, --list", "Output all modules in sorted order"));
+ out.println(String.format(" %-22s %s", "-d, --details", "Do class level analysis"));
+ }
+}
diff --git a/idp-admin-impl/src/main/java/net/shibboleth/idp/admin/impl/JarCheckCLI.java b/idp-admin-impl/src/main/java/net/shibboleth/idp/admin/impl/JarCheckCLI.java
new file mode 100644
index 000000000..75c7d4d3c
--- /dev/null
+++ b/idp-admin-impl/src/main/java/net/shibboleth/idp/admin/impl/JarCheckCLI.java
@@ -0,0 +1,274 @@
+/*
+ * 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.admin.impl;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.apache.commons.compress.archivers.ArchiveEntry;
+import org.apache.commons.compress.archivers.ArchiveInputStream;
+import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.idp.Version;
+import net.shibboleth.idp.cli.AbstractIdPHomeAwareCommandLine;
+
+/**
+ * Program to check for potential jar clashes.
+ */
+public final class JarCheckCLI extends AbstractIdPHomeAwareCommandLine<JarCheckArguments> {
+
+ /** Logger. */
+ @Nullable private Logger log;
+
+ /** Jar files in edit-webapp. */
+ @Nonnull private List<String> webAppJars = Collections.emptyList();
+
+ /** Jar files Plugin Folder. */
+ @Nonnull private List<String> pluginJars = Collections.emptyList();
+
+ /** Jar files Distributions . */
+ @Nonnull private List<String> distJars = Collections.emptyList();
+
+ /** Populate {@link #webAppJars}, {@link #pluginJars} and {@link #distJars}.
+ * @param args
+ */
+ private void loadJarFiles(final JarCheckArguments args) {
+ final Path idpHome = Path.of(args.getIdPHome());
+
+ distJars = listJars(idpHome.resolve("dist").resolve("webapp"));
+ pluginJars = listJars(idpHome.resolve("dist").resolve("plugin-webapp"));
+ webAppJars = listJars(idpHome.resolve("edit-webapp"));
+ }
+
+ /** return a list of all the files in the lib file below the webapp folder provided.
+ * @param webapp folder to start at.
+ * @return the list of names as a strung
+ */
+ private List<String> listJars(final Path webapp) {
+ final Path libDir = webapp.resolve("WEB-INF").resolve("lib");
+
+ if (!Files.exists(libDir) || !Files.isDirectory(libDir)) {
+ return Collections.emptyList();
+ }
+
+ return Arrays.asList(libDir.toFile().list(new FilenameFilter() {
+ public boolean accept(final File dir, final String name) {
+ final String nameUpper = name.toUpperCase();
+ return nameUpper.endsWith(".JAR");
+ }
+ }));
+ }
+
+ /** Check for the same file in two places and if asked for.
+ * output the sorted list
+ * @param args
+ */
+ private void listAndExactCheck(final JarCheckArguments args) {
+ final Map<String, String> allNames = new HashMap<>(distJars.size() + pluginJars.size() + webAppJars.size());
+ final String type = "jar called";
+
+ addAndCheck(allNames, distJars, "distribution", type);
+ addAndCheck(allNames, pluginJars, "plugins", type);
+ addAndCheck(allNames, webAppJars, "edit-webapp", type);
+
+ if (!args.isList()) {
+ return;
+ }
+
+ final List<String> names = new ArrayList<>(allNames.keySet());
+ Collections.sort(names);
+ System.out.println("Sorted List of jars");
+ System.out.println(String.format(" %-22s %s", "Name", "Source"));
+ for (final String jarName: names) {
+ System.out.println(String.format(" %-22s %s", jarName, allNames.get(jarName)));
+ }
+ }
+
+ /** Add the names to the Map, checking for already existing.
+ * @param allNames the map
+ * @param jars the names
+ * @param source where the jar came from
+ * @param type what we are checking
+ */
+ private void addAndCheck(final Map<String, String> allNames,
+ final List<String> jars,
+ final String source,
+ final String type) {
+
+ for (final String jar: jars) {
+ final String current = allNames.get(jar);
+ if (current == null) {
+ allNames.put(jar, source);
+ } else {
+ getLogger().warn("{} {} found in {} and in {}", type, jar, source, current);
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ protected Class<JarCheckArguments> getArgumentClass() {
+ return JarCheckArguments.class;
+ }
+
+ /** {@inheritDoc} */
+ protected String getVersion() {
+ return Version.getVersion();
+ }
+
+ /** {@inheritDoc} */
+ protected synchronized Logger getLogger() {
+ if (log == null) {
+ log = LoggerFactory.getLogger(JarCheckCLI.class);
+ }
+ return log;
+ }
+
+ /** Do a general test.
+ * @param args
+ */
+ private void fileNamesOnly(final JarCheckArguments args) {
+ final Map<String, String> allNames = new HashMap<>(distJars.size() + pluginJars.size() + webAppJars.size());
+ final String type = "jar fragment called";
+
+ addAndCheck(allNames, normalize(distJars), "distribution", type);
+ addAndCheck(allNames, normalize(pluginJars), "plugins", type);
+ addAndCheck(allNames, normalize(webAppJars), "edit-webapp", type);
+ }
+
+ /** make the file names look more 'usual'.
+ * @param fileNames
+ * @return the processed names
+ */
+ private List<String> normalize(final List<String> fileNames) {
+
+ final Pattern patt = Pattern.compile("^(.*)-[0123456789](.*)\\.jar$");
+ final List<String> result = new ArrayList<>(fileNames.size());
+
+ for (final String name: fileNames ) {
+ final Matcher matcher = patt.matcher(name);
+ if (matcher.find()) {
+ result.add(matcher.group(1));
+ }
+ }
+ return result;
+ }
+
+ /** Do a detailed check.
+ * @param args
+ */
+ private void detailed(final JarCheckArguments args) {
+ final Map<String, String> allNames = new HashMap<>();
+ final Path idpHome = Path.of(args.getIdPHome());
+
+ processClassNames(allNames, idpHome.resolve("dist").resolve("webapp"), distJars);
+ processClassNames(allNames, idpHome.resolve("dist").resolve("plugin-webapp"), pluginJars);
+ processClassNames(allNames, idpHome.resolve("edit-webapp"), webAppJars);
+ }
+
+ /** Look at all the class names in the jars provided.
+ * @param namesSoFar the class names we have found
+ * @param base The directory where the names live
+ * @param jars The Jar Names
+ */
+ private void processClassNames(final Map<String, String> namesSoFar, final Path base, final List<String> jars) {
+ final Path libdir = base.resolve("WEB-INF").resolve("lib");
+ for (final String jar: jars) {
+ processClassNames(namesSoFar, libdir.resolve(jar));
+ }
+ }
+
+ /** List all the class names in the jar provided.
+ * @param namesSoFar the class names we have found
+ * @param jar The far file
+ */
+ private void processClassNames(final Map<String, String> namesSoFar, final Path jar) {
+ final String source = jar.toString();
+ try (final InputStream inStream = new BufferedInputStream(new FileInputStream(jar.toFile()));
+ final ArchiveInputStream classes = new ZipArchiveInputStream(inStream)) {
+
+ ArchiveEntry entry = null;
+ while ((entry = classes.getNextEntry()) != null) {
+ if (entry.isDirectory()) {
+ continue;
+ }
+ final String claz = entry.getName();
+ if (claz == null || claz.startsWith("META-INF") || "module-info.class".equals(claz)) {
+ continue;
+ }
+ final String current = namesSoFar.get(claz);
+ if (current == null) {
+ namesSoFar.put(claz, source);
+ } else {
+ getLogger().warn("class {} found in {} and in {}", claz, source, current);
+ }
+ }
+ } catch (final FileNotFoundException e) {
+ getLogger().error("Could not open {}", source, e);
+ } catch (final IOException e) {
+ getLogger().error("Could not process {}", source, e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected int doRun(@Nonnull final JarCheckArguments args) {
+ final int ret = super.doRun(args);
+ if (ret != RC_OK) {
+ return ret;
+ }
+ loadJarFiles(args);
+ listAndExactCheck(args);
+ if (args.isDetailed()) {
+ detailed(args);
+ } else {
+ fileNamesOnly(args);
+ }
+
+ return RC_OK;
+ }
+
+ /**
+ * CLI entry point.
+ *
+ * @param args arguments
+ */
+ public static void main(@Nonnull final String[] args) {
+ System.exit(new JarCheckCLI().run(args));
+ }
+
+}
diff --git a/idp-installer/pom.xml b/idp-installer/pom.xml
index 114a64654..74f1a1818 100644
--- a/idp-installer/pom.xml
+++ b/idp-installer/pom.xml
@@ -140,7 +140,6 @@
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
- <version>${commons.compress.version}</version>
<scope>provided</scope>
</dependency>
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list