@Nullable
  @VisibleForTesting
  static List<String> computeEntrypoint(
      RawConfiguration rawConfiguration,
      ProjectProperties projectProperties,
      JibContainerBuilder jibContainerBuilder)
      throws MainClassInferenceException, InvalidAppRootException, IOException,
          InvalidContainerizingModeException {
    Optional<List<String>> rawEntrypoint = rawConfiguration.getEntrypoint();
    List<String> rawExtraClasspath = rawConfiguration.getExtraClasspath();
    boolean entrypointDefined = rawEntrypoint.isPresent() && !rawEntrypoint.get().isEmpty();

    if (entrypointDefined
        && (rawConfiguration.getMainClass().isPresent()
            || !rawConfiguration.getJvmFlags().isEmpty()
            || !rawExtraClasspath.isEmpty()
            || rawConfiguration.getExpandClasspathDependencies())) {
      projectProperties.log(
          LogEvent.info(
              "mainClass, extraClasspath, jvmFlags, and expandClasspathDependencies are ignored "
                  + "when entrypoint is specified"));
    }

    if (projectProperties.isWarProject()) {
      if (entrypointDefined) {
        return rawEntrypoint.get().size() == 1 && "INHERIT".equals(rawEntrypoint.get().get(0))
            ? null
            : rawEntrypoint.get();
      }

      if (rawConfiguration.getMainClass().isPresent()
          || !rawConfiguration.getJvmFlags().isEmpty()
          || !rawExtraClasspath.isEmpty()
          || rawConfiguration.getExpandClasspathDependencies()) {
        projectProperties.log(
            LogEvent.warn(
                "mainClass, extraClasspath, jvmFlags, and expandClasspathDependencies are ignored "
                    + "for WAR projects"));
      }
      return rawConfiguration.getFromImage().isPresent()
          ? null // Inherit if a custom base image.
          : Arrays.asList("java", "-jar", "/usr/local/jetty/start.jar", "--module=ee10-deploy");
    }

    List<String> classpath = new ArrayList<>(rawExtraClasspath);
    AbsoluteUnixPath appRoot = getAppRootChecked(rawConfiguration, projectProperties);
    ContainerizingMode mode = getContainerizingModeChecked(rawConfiguration, projectProperties);
    switch (mode) {
      case EXPLODED:
        classpath.add(appRoot.resolve("resources").toString());
        classpath.add(appRoot.resolve("classes").toString());
        break;
      case PACKAGED:
        classpath.add(appRoot.resolve("classpath/*").toString());
        break;
      default:
        throw new IllegalStateException("unknown containerizing mode: " + mode);
    }

    if (projectProperties.getMajorJavaVersion() >= 9
        || rawConfiguration.getExpandClasspathDependencies()) {
      List<Path> jars = projectProperties.getDependencies();

      Map<String, Long> occurrences =
          jars.stream()
              .map(path -> path.getFileName().toString())
              .collect(Collectors.groupingBy(filename -> filename, Collectors.counting()));
      List<String> duplicates =
          occurrences.entrySet().stream()
              .filter(entry -> entry.getValue() > 1)
              .map(Map.Entry::getKey)
              .collect(Collectors.toList());

      for (Path jar : jars) {
        // Handle duplicates by appending filesize to the end of the file. This renaming logic
        // must be in sync with the code that does the same in the other place. See
        // https://github.com/GoogleContainerTools/jib/issues/3331
        String jarName = jar.getFileName().toString();
        if (duplicates.contains(jarName)) {
          jarName = jarName.replaceFirst("\\.jar$", "-" + Files.size(jar)) + ".jar";
        }
        classpath.add(appRoot.resolve("libs").resolve(jarName).toString());
      }
    } else {
      classpath.add(appRoot.resolve("libs/*").toString());
    }

    String classpathString = String.join(":", classpath);
    String mainClass;
    try {
      mainClass =
          MainClassResolver.resolveMainClass(
              rawConfiguration.getMainClass().orElse(null), projectProperties);
    } catch (MainClassInferenceException ex) {
      if (entrypointDefined) {
        // We will use the user-given entrypoint, so don't fail.
        mainClass = "could-not-infer-a-main-class";
      } else {
        throw ex;
      }
    }
    addJvmArgFilesLayer(
        rawConfiguration, projectProperties, jibContainerBuilder, classpathString, mainClass);

    if (projectProperties.getMajorJavaVersion() >= 9) {
      classpathString = "@" + appRoot.resolve(JIB_CLASSPATH_FILE);
    }

    if (entrypointDefined) {
      return rawEntrypoint.get().size() == 1 && "INHERIT".equals(rawEntrypoint.get().get(0))
          ? null
          : rawEntrypoint.get();
    }

    List<String> entrypoint = new ArrayList<>(4 + rawConfiguration.getJvmFlags().size());
    entrypoint.add("java");
    entrypoint.addAll(rawConfiguration.getJvmFlags());
    entrypoint.add("-cp");
    entrypoint.add(classpathString);
    entrypoint.add(mainClass);
    return entrypoint;
  }
@Test
  public void testComputeEntrypoint_expandClasspathDependencies()
      throws MainClassInferenceException, InvalidAppRootException, IOException,
          InvalidContainerizingModeException {
    when(rawConfiguration.getExpandClasspathDependencies()).thenReturn(true);
    assertThat(
            PluginConfigurationProcessor.computeEntrypoint(
                rawConfiguration, projectProperties, jibContainerBuilder))
        .containsExactly(
            "java",
            "-cp",
            "/app/resources:/app/classes:/app/libs/foo-1.jar:/app/libs/bar-2.jar",
            "java.lang.Object")
        .inOrder();
  }