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);
}