Repository: GoogleContainerTools/jib
File: jib-cli/src/main/java/com/google/cloud/tools/jib/cli/jar/JarModeProcessor.java (Java)

Code before:
    // Get dependencies from Class-Path in the jar's manifest and add a layer each for non-snapshot
    // and snapshot dependencies. If Class-Path is not present in the jar's manifest then skip
    // adding the dependencies layers.
    String classPath = null;
    try (JarFile jarFile = new JarFile(jarPath.toFile())) {
      classPath = jarFile.getManifest().getMainAttributes().getValue(Attributes.Name.CLASS_PATH);
    }
    if (classPath != null) {
      Predicate<String> isSnapshot = name -> name.contains("SNAPSHOT");
      List<String> allDependencies = Splitter.onPattern("\\s+").splitToList(classPath.trim());
      List<Path> nonSnapshotDependencies =
          allDependencies
              .stream()
              .filter(isSnapshot.negate())
              .map(Paths::get)
              .collect(Collectors.toList());
      List<Path> snapshotDependencies =
          allDependencies.stream().filter(isSnapshot).map(Paths::get).collect(Collectors.toList());
      Path jarParent = jarPath.getParent() == null ? Paths.get("") : jarPath.getParent();
      if (!nonSnapshotDependencies.isEmpty()) {
        FileEntriesLayer.Builder nonSnapshotDependenciesLayerBuilder =
            FileEntriesLayer.builder().setName(DEPENDENCIES);
        nonSnapshotDependencies.forEach(
            path ->
                nonSnapshotDependenciesLayerBuilder.addEntry(
                    jarParent.resolve(path),
                    APP_ROOT
                        .resolve(RelativeUnixPath.get("dependencies"))
                        .resolve(path.getFileName())));
        layers.add(nonSnapshotDependenciesLayerBuilder.build());
      }
      if (!snapshotDependencies.isEmpty()) {
        FileEntriesLayer.Builder snapshotDependenciesLayerBuilder =
            FileEntriesLayer.builder().setName(SNAPSHOT_DEPENDENCIES);
        snapshotDependencies.forEach(
            path ->
                snapshotDependenciesLayerBuilder.addEntry(
                    jarParent.resolve(path),
                    APP_ROOT
                        .resolve(RelativeUnixPath.get("dependencies"))
                        .resolve(path.getFileName())));
        layers.add(snapshotDependenciesLayerBuilder.build());
      }
    }

    // Determine class and resource files in the directory containing jar contents and create
    // FileEntriesLayer for each type of layer (classes or resources), while maintaining the
    // file's original project structure.
    Predicate<Path> isClassFile = path -> path.getFileName().toString().endsWith(".class");
    Predicate<Path> isResourceFile = isClassFile.negate();
    FileEntriesLayer classesLayer =

Review comment (bug):
I just realized one thing we are missing. Remember [this output](https://github.com/GoogleContainerTools/jib/pull/2888#discussion_r520255662)? A lot of people will try Jib CLI on a standard JAR without having dependency JARs at all or in right relative locations, and they will just see `java.nio.file.NoSuchFileException: some.jar`. And they may falsely think Jib CLI is bugged by failing to load its dependency. I think it's very important to check the existence of dependency JARs locally before we delegate to jib-core, and throw an exception (`IllegalStateException`?) with a detailed message that some dependency JARs of the user app don't exist locally. Doesn't have to be in this PR, but we should not forget this.

Code after:
    // and snapshot dependencies. If Class-Path is not present in the jar's manifest then skip
    // adding the dependencies layers.
    String classPath = null;
    try (JarFile jarFile = new JarFile(jarPath.toFile())) {
      classPath = jarFile.getManifest().getMainAttributes().getValue(Attributes.Name.CLASS_PATH);
    }
    if (classPath != null) {
      Predicate<String> isSnapshot = name -> name.contains("SNAPSHOT");
      List<String> allDependencies = Splitter.onPattern("\\s+").splitToList(classPath.trim());
      List<Path> nonSnapshotDependencies =
          allDependencies
              .stream()
              .filter(isSnapshot.negate())
              .map(Paths::get)
              .collect(Collectors.toList());
      List<Path> snapshotDependencies =
          allDependencies.stream().filter(isSnapshot).map(Paths::get).collect(Collectors.toList());
      Path jarParent = jarPath.getParent() == null ? Paths.get("") : jarPath.getParent();
      if (!nonSnapshotDependencies.isEmpty()) {
        FileEntriesLayer.Builder nonSnapshotDependenciesLayerBuilder =
            FileEntriesLayer.builder().setName(DEPENDENCIES);
        nonSnapshotDependencies.forEach(
            path ->
                addDependency(
                    nonSnapshotDependenciesLayerBuilder,
                    jarParent.resolve(path),
                    APP_ROOT.resolve("dependencies").resolve(path.getFileName())));
        layers.add(nonSnapshotDependenciesLayerBuilder.build());
      }
      if (!snapshotDependencies.isEmpty()) {
        FileEntriesLayer.Builder snapshotDependenciesLayerBuilder =
            FileEntriesLayer.builder().setName(SNAPSHOT_DEPENDENCIES);
        snapshotDependencies.forEach(
            path ->
                addDependency(
                    snapshotDependenciesLayerBuilder,
                    jarParent.resolve(path),
                    APP_ROOT.resolve("dependencies").resolve(path.getFileName())));
        layers.add(snapshotDependenciesLayerBuilder.build());
      }
    }

    // Determine class and resource files in the directory containing jar contents and create
    // FileEntriesLayer for each type of layer (classes or resources), while maintaining the
    // file's original project structure.
    Predicate<Path> isClassFile = path -> path.getFileName().toString().endsWith(".class");
    Predicate<Path> isResourceFile = isClassFile.negate();
    FileEntriesLayer classesLayer =
        addDirectoryContentsToLayer(
            CLASSES, localExplodedJarRoot, isClassFile, APP_ROOT.resolve("explodedJar"));
    FileEntriesLayer resourcesLayer =