private ClassLoader createClassLoader()

in runtime/impl/src/main/java/com/google/apphosting/runtime/AppVersionFactory.java [380:522]


  private ClassLoader createClassLoader(
      ApplicationEnvironment environment,
      File root,
      AppInfo appInfo,
      AppEngineWebXml appEngineWebXml)
      throws IOException {
    ClassPathUtils classPathUtils = sandboxPlugin.getClassPathUtils();

    ClassPathBuilder classPathBuilder =
        new ClassPathBuilder(appEngineWebXml.getClassLoaderConfig());

    // From the servlet spec, SRV.9.5 "The Web application class loader must load
    // classes from the WEB-INF/ classes directory first, and then from library JARs
    // in the WEB-INF/lib directory."
    try {
      File classes = new File(new File(root, "WEB-INF"), "classes");
      if (classes.isDirectory()) {
        classPathBuilder.addClassesUrl(classes.toURI().toURL());
      }
    } catch (MalformedURLException ex) {
      logger.atWarning().withCause(ex).log("Could not add WEB-INF/classes");
    }

    // If there is an API version specified in the AppInfo, then the
    // user code does not include the API code and we need to append
    // our own copy of the requested API version.
    //
    // N.B.: The API version jar should come after
    // WEB-INF/classes (to avoid violating the servlet spec) but
    // before other WEB-INF/lib jars (in case the user is including
    // appengine-tools-api.jar or something else superfluous).
    // Old apps would still use an ApiVersion of 1.0 (inside manifest of old api jar) and would
    // not bundled the api jar.
    // Newer apps are now using "user_defined" and they bundle the api jar, so no need to add it.
    // App not using the api jar would have "none" or empty.
    // Read ../runtime/jetty9/AppInfoFactory.java to see where it is setup.
    String apiVersion = appInfo.getApiVersion();
    if (!apiVersion.isEmpty()
        && !Ascii.equalsIgnoreCase(apiVersion, "none")
        && !Ascii.equalsIgnoreCase(apiVersion, "user_defined")) {
      // For now, the only valid version has been "1.0" since the beginning of App Engine.

      if (classPathUtils == null) {
        logger.atInfo().log("Ignoring API version setting %s", apiVersion);
      } else {
        File apiJar = classPathUtils.getFrozenApiJar();
        if (apiJar != null) {
          logger.atInfo().log("Adding API jar %s for version %s", apiJar, apiVersion);
          try {
            classPathBuilder.addAppengineJar(new URL("file", "", apiJar.getAbsolutePath()));
          } catch (MalformedURLException ex) {
            logger.atWarning().withCause(ex).log("Could not parse URL for %s, ignoring.", apiJar);
          }

          File appengineApiLegacyJar = classPathUtils.getAppengineApiLegacyJar();
          if (appengineApiLegacyJar != null) {
            logger.atInfo().log("Adding appengine-api-legacy jar %s", appengineApiLegacyJar);
            try {
              // Add appengine-api-legacy jar with appengine-api-jar priority.
              classPathBuilder.addAppengineJar(
                  new URL("file", "", appengineApiLegacyJar.getAbsolutePath()));
            } catch (MalformedURLException ex) {
              logger.atWarning().withCause(ex).log(
                  "Could not parse URL for %s, ignoring.", appengineApiLegacyJar);
            }
          }
        } else {
          // TODO: We should probably return an
          // UPResponse::UNKNOWN_API_VERSION here, but I'd like to be
          // lenient until API versions are well established.
          logger.atWarning().log(
              "The Java runtime is not adding an API jar for this application, as the "
                  + "Java api_version defined in app.yaml or appinfo is unknown: %s",
              apiVersion);
        }
      }
    }
    if (!appInfo.getFileList().isEmpty()) {
      for (AppInfo.File appFile : appInfo.getFileList()) {
        File file = new File(root, appFile.getPath());
        // _ah*.jar are jars for classes or jsps from the classes directory.
        // Treat them as if they are from WEB-INF/classes, per servlet specification.
        if (appFile.getPath().startsWith("WEB-INF/lib/_ah")) {
          try {
            classPathBuilder.addClassesUrl(new URL("file", "", file.getAbsolutePath()));
          } catch (MalformedURLException ex) {
            logger.atWarning().withCause(ex).log("Could not get URL for file: %s", file);
          }
        } else if (appFile.getPath().startsWith("WEB-INF/lib/")) {
          try {
            classPathBuilder.addAppJar(new URL("file", "", file.getAbsolutePath()));
          } catch (MalformedURLException ex) {
            logger.atWarning().withCause(ex).log("Could not get URL for file: %s", file);
          }
        }
      }
    } else {
      Path pathToLib = FileSystems.getDefault().getPath(root.getAbsolutePath(), "WEB-INF", "lib");
      if (pathToLib.toFile().isDirectory()) {
        // Search for jar regular files only under 1 level under WEB-INF/lib
        // FOLLOW_LINKS is there in case WEB-INF/lib is itself a symbolic link.
        try (Stream<Path> stream = Files.walk(pathToLib, 1, FOLLOW_LINKS)) {
          stream
              .filter(Files::isRegularFile)
              .forEach(
                  path -> {

                    // _ah*.jar are jars for classes or jsps from the classes directory.
                    // Treat them as if they are from WEB-INF/classes, per servlet specification.
                    if (path.getFileName().toString().startsWith("_ah")) {
                      try {
                        classPathBuilder.addClassesUrl(new URL("file", "", path.toString()));
                      } catch (MalformedURLException ex) {
                        logger.atWarning().withCause(ex).log(
                            "Could not get URL for file: %s", path);
                      }
                    } else {
                      try {
                        classPathBuilder.addAppJar(new URL("file", "", path.toString()));
                      } catch (MalformedURLException ex) {
                        logger.atWarning().withCause(ex).log(
                            "Could not get URL for file: %s", path);
                      }
                    }
                  });
        }
      }
    }

    switch (appEngineWebXml.getUseGoogleConnectorJ()) {
      case NOT_STATED_BY_USER:
        environment.setUseGoogleConnectorJ(null);
        break;
      case TRUE:
        environment.setUseGoogleConnectorJ(true);
        break;
      case FALSE:
        environment.setUseGoogleConnectorJ(false);
        break;
    }

    return sandboxPlugin.createApplicationClassLoader(getUrls(classPathBuilder), root, environment);
  }