All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.redhat.insights.jars.JarUtils Maven / Gradle / Ivy

There is a newer version: 2.0.4
Show newest version
/* Copyright (C) Red Hat 2023 */
package com.redhat.insights.jars;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.security.DigestInputStream;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.jar.Manifest;
import java.util.stream.Stream;

public final class JarUtils {

  private static final Map EMBEDDED_FORMAT_TO_EXTENSION =
      getEmbeddedFormatToExtension("ear", "war", "jar");
  private static final int DEFAULT_BUFFER_SIZE = 1024 * 8;
  private static final String JAR_PROTOCOL = "jar:";
  private static final String BANG_SEPARATOR = "!/";

  private JarUtils() {}

  private static Map getEmbeddedFormatToExtension(String... fileExtensions) {
    Map out = new HashMap<>();
    Stream.of(fileExtensions).forEach(ext -> out.put('.' + ext + BANG_SEPARATOR, ext));
    return out;
  }

  /**
   * Open an input stream for the given url. If the url points to a jar within a jar, return an
   * input stream starting at the embedded jar.
   *
   * @param url
   * @return a Stream to the specified URL.
   * @throws IOException
   */
  public static InputStream getInputStream(URL url) throws IOException {
    String jarLocation = url.toExternalForm();
    URL jarURL = url;
    for (Entry entry : EMBEDDED_FORMAT_TO_EXTENSION.entrySet()) {
      int index = jarLocation.indexOf(entry.getKey());
      // if the target is the jar file then we can't use openStream as it is there to look into the
      // jar content.
      if (index > 0 && (index + entry.getKey().length()) < jarLocation.length()) {
        String path = url.toExternalForm().substring(index + entry.getKey().length());
        if (path.endsWith(BANG_SEPARATOR)) {
          path = path.substring(0, path.length() - BANG_SEPARATOR.length());
        }
        // add 1 to skip past the `.` and the value length, which is the length of the file
        // extension
        String jar = jarURL.toExternalForm().substring(0, index + 1 + entry.getValue().length());
        if (jarLocation.startsWith(JAR_PROTOCOL)) {
          jar = jar.substring(JAR_PROTOCOL.length());
        }
        jarURL = new URL(jar);
        InputStream inputStream = jarURL.openStream();
        JarInputStream jarStream = new JarInputStream(inputStream);

        if (!readToEntry(jarStream, path)) {
          inputStream.close();
          throw new IOException(
              "Unable to open stream for " + path + " in " + jarURL.toExternalForm());
        }
        return jarStream;
      }
    }
    if (jarLocation.startsWith(JAR_PROTOCOL) && jarLocation.endsWith(BANG_SEPARATOR)) {
      String jarLoc = jarLocation.substring(4, jarLocation.length() - BANG_SEPARATOR.length());
      jarURL = new URL(jarLoc);
    }
    return jarURL.openStream();
  }

  /**
   * Read a jar input stream until a given path is found.
   *
   * @param jarStream
   * @param path
   * @return true if the path was found
   * @throws IOException
   */
  private static boolean readToEntry(JarInputStream jarStream, String path) throws IOException {
    String folderPath = path.endsWith("/") ? path : path + '/';
    for (JarEntry jarEntry = null; (jarEntry = jarStream.getNextJarEntry()) != null; ) {
      if (path.equals(jarEntry.getName()) || folderPath.equals(jarEntry.getName())) {
        return true;
      }
    }
    return false;
  }

  static JarInputStream getJarInputStream(URL url) throws IOException {
    boolean isEmbedded = isEmbedded(url);
    InputStream is = getInputStream(url);
    if (!isEmbedded && is instanceof JarInputStream) {
      return (JarInputStream) is;
    }
    return new JarInputStream(is);
  }

  private static boolean isEmbedded(URL url) {
    String externalForm = url.toExternalForm();
    for (String prefix : EMBEDDED_FORMAT_TO_EXTENSION.keySet()) {
      if (externalForm.contains(prefix)) {
        return true;
      }
    }

    return false;
  }

  /////////////////////////////////////////////////
  // Handle manifests
  public static String getVersionFromManifest(Manifest manifest) {
    String version = getVersion(manifest.getMainAttributes());
    if (version == null && !manifest.getEntries().isEmpty()) {
      version = getVersion(manifest.getEntries().values().iterator().next());
    }

    return version;
  }

  private static String getVersion(Attributes attributes) {
    String version = attributes.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
    if (version == null) {
      version = attributes.getValue(Attributes.Name.SPECIFICATION_VERSION);
      if (version == null) {
        // lots of jars specify their version in the Bundle-Version. I believe it's an OSGi thing
        version = attributes.getValue("Bundle-Version");
      }
      if (version == null) {
        // some JDBC drivers specify their version in Driver-Version
        version = attributes.getValue("Driver-Version");
      }
    }
    return version;
  }

  //////////////////////////////////////////
  // Handle hashes / digests
  public static String computeSha1(URL url) throws NoSuchAlgorithmException, IOException {
    return computeSha(url, "SHA1");
  }

  public static String computeSha256(URL url) throws NoSuchAlgorithmException, IOException {
    return computeSha(url, "SHA-256");
  }

  public static String computeSha512(URL url) throws NoSuchAlgorithmException, IOException {
    return computeSha(url, "SHA-512");
  }

  public static String computeSha256(final byte[] buffer)
      throws NoSuchAlgorithmException, IOException {
    return computeSha(buffer, "SHA-256");
  }

  public static String computeSha512(final byte[] buffer)
      throws NoSuchAlgorithmException, IOException {
    return computeSha(buffer, "SHA-512");
  }

  static String computeSha(URL url, String algorithm) throws NoSuchAlgorithmException, IOException {
    final InputStream inputStream = JarUtils.getInputStream(url);
    return computeSha(inputStream, algorithm);
  }

  public static String computeSha(InputStream inputStream, String algorithm)
      throws NoSuchAlgorithmException, IOException {
    final MessageDigest md = MessageDigest.getInstance(algorithm);
    final int readLen = 128;
    try (final DigestInputStream dis = new DigestInputStream(inputStream, md)) {
      final byte[] readBytes = new byte[readLen];
      while (dis.read(readBytes) != -1) {}
    }
    return toHex(md.digest());
  }

  public static String computeSha(byte[] buffer, String algorithm) throws NoSuchAlgorithmException {
    final MessageDigest md = MessageDigest.getInstance(algorithm);
    return toHex(md.digest(buffer));
  }

  private static String toHex(byte[] bytes) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < bytes.length; i++) {
      sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
    }
    return sb.toString();
  }

  public static String[] computeSha(URL url) throws NoSuchAlgorithmException, IOException {
    try (final InputStream inputStream = JarUtils.getInputStream(url)) {
      return computeSha(inputStream);
    }
  }

  public static final String[] computeSha(InputStream inputStream)
      throws NoSuchAlgorithmException, IOException {
    final MessageDigest mdSha1 = MessageDigest.getInstance("SHA1");
    final MessageDigest mdSha256 = MessageDigest.getInstance("SHA-256");
    final MessageDigest mdSha512 = MessageDigest.getInstance("SHA-512");

    try (final DigestOutputStream outSha1 = new DigestOutputStream(nullOutputStream(), mdSha1);
        final DigestOutputStream outSha256 = new DigestOutputStream(nullOutputStream(), mdSha256);
        final DigestOutputStream outSha512 = new DigestOutputStream(nullOutputStream(), mdSha512)) {
      int nRead;
      byte[] data = new byte[4096];
      while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
        outSha1.write(data, 0, nRead);
        outSha256.write(data, 0, nRead);
        outSha512.write(data, 0, nRead);
      }
    }
    return new String[] {
      toHex(mdSha1.digest()), toHex(mdSha256.digest()), toHex(mdSha512.digest())
    };
  }

  public static OutputStream nullOutputStream() {
    return new OutputStream() {
      private volatile boolean closed;

      private void ensureOpen() throws IOException {
        if (closed) {
          throw new IOException("Stream closed");
        }
      }

      @Override
      public void write(int b) throws IOException {
        ensureOpen();
      }

      @Override
      public void write(byte b[], int off, int len) throws IOException {
        ensureOpen();
      }

      @Override
      public void close() {
        closed = true;
      }
    };
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy