com.google.apphosting.runtime.JavaRuntime Maven / Gradle / Ivy
/*
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.apphosting.runtime;
import static com.google.common.base.StandardSystemProperty.JAVA_SPECIFICATION_VERSION;
import com.google.apphosting.base.AppVersionKey;
import com.google.apphosting.base.protos.AppinfoPb.AppInfo;
import com.google.apphosting.base.protos.EmptyMessage;
import com.google.apphosting.base.protos.RuntimePb.UPAddDelete;
import com.google.apphosting.base.protos.RuntimePb.UPRequest;
import com.google.apphosting.base.protos.RuntimePb.UPResponse;
import com.google.apphosting.runtime.anyrpc.AnyRpcPlugin;
import com.google.apphosting.runtime.anyrpc.AnyRpcServerContext;
import com.google.apphosting.runtime.anyrpc.EvaluationRuntimeServerInterface;
import com.google.apphosting.utils.config.AppEngineWebXml;
import com.google.auto.value.AutoBuilder;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.flogger.GoogleLogger;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.SynchronousQueue;
import javax.annotation.Nullable;
/**
* JavaRuntime implements the Prometheus EvaluationRuntime service. It handles any requests for the
* "java" runtime. At the moment, this only includes requests whose handler type is SERVLET. The
* handler path is assumed to be the full class name of a class that extends {@link
* javax.servlet.GenericServlet}.
*
* {@code JavaRuntime} is not responsible for configuring {@code ApiProxy} with a delegate. This
* class should probably be instantiated by {@code JavaRuntimeFactory}, which also sets up {@code
* ApiProxy}.
*
*/
public class JavaRuntime implements EvaluationRuntimeServerInterface {
private static final GoogleLogger logger = GoogleLogger.forEnclosingClass();
/** Environment Variable for logging messages to /var/log. */
private static final String VAR_LOG_ENV_VAR = "WRITE_LOGS_TO_VAR_LOG";
/** Environment variable for the GCP project. */
private static final String GOOGLE_CLOUD_PROJECT_ENV_VAR = "GOOGLE_CLOUD_PROJECT";
/** Default directory for JSON-formatted logs. */
private static final Path DEFAULT_JSON_LOG_OUTPUT_DIR = Paths.get("/var/log");
/** Filename for JSON-formatted logs. */
private static final String JSON_LOG_OUTPUT_FILE = "app";
/**
* ServletEngineAdapter is a wrapper around the servlet engine to whom we are deferring servlet
* lifecycle and request/response management.
*/
private final ServletEngineAdapter servletEngine;
/** Sandbox-agnostic plugin. */
private final NullSandboxPlugin sandboxPlugin;
/** RPC-agnostic plugin. */
private final AnyRpcPlugin rpcPlugin;
/** {@code AppVersionFactory} can construct {@link AppVersion} instances. */
private final AppVersionFactory appVersionFactory;
/** Handles request setup and tear-down. */
private final RequestManager requestManager;
/** The string that should be returned by {@code ServletContext.getServerInfo()}. */
private final String runtimeVersion;
/** A template runtime configuration for applications. */
private final ApplicationEnvironment.RuntimeConfiguration templateConfiguration;
/** The object responsible for choosing API call deadlines. */
private final ApiDeadlineOracle deadlineOracle;
private final Logging logging = new Logging();
private final BackgroundRequestCoordinator coordinator;
private final boolean compressResponse;
private final boolean enableHotspotPerformanceMetrics;
private final boolean pollForNetwork;
private final boolean redirectStdoutStderr;
private final boolean logJsonToFile;
private final boolean clearLogHandlers;
private final Path jsonLogDir;
/**
* This will contain a reference to the ByteBuffer containing Hotspot performance data, exported
* by the sun.misc.Perf api in Java 8 and by jdk.internal.perf.Perf in Java 9. It's set once and
* for all when start() is called.
*/
private ByteBuffer hotspotPerformanceData = null;
/**
* The app version that has been received by this runtime, or null if no version has been received
* yet. We only ever receive one version.
*/
private AppVersion appVersion;
/** Get a partly-initialized builder. */
public static Builder builder() {
return new AutoBuilder_JavaRuntime_Builder()
.setCompressResponse(true)
.setEnableHotspotPerformanceMetrics(true)
.setPollForNetwork(false)
.setDefaultToNativeUrlStreamHandler(false)
.setForceUrlfetchUrlStreamHandler(false)
.setIgnoreDaemonThreads(true)
.setUseEnvVarsFromAppInfo(false)
.setFixedApplicationPath(null)
.setRedirectStdoutStderr(true)
.setLogJsonToFile(false)
.setClearLogHandlers(true)
.setJsonLogDir(DEFAULT_JSON_LOG_OUTPUT_DIR);
}
/** Builder for JavaRuntime. */
@AutoBuilder
public abstract static class Builder {
Builder() {}
public abstract Builder setServletEngine(ServletEngineAdapter servletEngine);
public abstract ServletEngineAdapter servletEngine();
public abstract Builder setSandboxPlugin(NullSandboxPlugin sandboxPlugin);
public abstract Builder setRpcPlugin(AnyRpcPlugin rpcPlugin);
public abstract AnyRpcPlugin rpcPlugin();
public abstract Builder setSharedDirectory(File sharedDirectory);
public abstract File sharedDirectory();
public abstract Builder setRequestManager(RequestManager requestManager);
public abstract Builder setRuntimeVersion(String runtimeVersion);
public abstract String runtimeVersion();
public abstract Builder setConfiguration(
ApplicationEnvironment.RuntimeConfiguration configuration);
public abstract ApplicationEnvironment.RuntimeConfiguration configuration();
public abstract Builder setDeadlineOracle(ApiDeadlineOracle deadlineOracle);
public abstract ApiDeadlineOracle deadlineOracle();
public abstract Builder setCoordinator(BackgroundRequestCoordinator coordinator);
public abstract Builder setCompressResponse(boolean compressResponse);
public abstract boolean compressResponse();
public abstract Builder setEnableHotspotPerformanceMetrics(
boolean enableHotspotPerformanceMetrics);
public abstract boolean enableHotspotPerformanceMetrics();
public abstract Builder setPollForNetwork(boolean pollForNetwork);
public abstract boolean pollForNetwork();
public abstract Builder setDefaultToNativeUrlStreamHandler(
boolean defaultToNativeUrlStreamHandler);
public abstract boolean defaultToNativeUrlStreamHandler();
public abstract Builder setForceUrlfetchUrlStreamHandler(boolean forceUrlfetchUrlStreamHandler);
public abstract boolean forceUrlfetchUrlStreamHandler();
public abstract Builder setIgnoreDaemonThreads(boolean ignoreDaemonThreads);
public abstract boolean ignoreDaemonThreads();
public abstract Builder setUseEnvVarsFromAppInfo(boolean useEnvVarsFromAppInfo);
public abstract boolean useEnvVarsFromAppInfo();
public abstract Builder setFixedApplicationPath(String fixedApplicationPath);
public abstract String fixedApplicationPath();
public abstract Builder setRedirectStdoutStderr(boolean redirect);
public abstract boolean redirectStdoutStderr();
public abstract Builder setLogJsonToFile(boolean log);
public abstract boolean logJsonToFile();
public abstract Builder setClearLogHandlers(boolean log);
public abstract Builder setJsonLogDir(Path path);
public abstract Path jsonLogDir();
public abstract JavaRuntime build();
}
JavaRuntime(
ServletEngineAdapter servletEngine,
NullSandboxPlugin sandboxPlugin,
AnyRpcPlugin rpcPlugin,
File sharedDirectory,
RequestManager requestManager,
String runtimeVersion,
ApplicationEnvironment.RuntimeConfiguration configuration,
ApiDeadlineOracle deadlineOracle,
BackgroundRequestCoordinator coordinator,
boolean compressResponse,
boolean enableHotspotPerformanceMetrics,
boolean pollForNetwork,
boolean defaultToNativeUrlStreamHandler,
boolean forceUrlfetchUrlStreamHandler,
boolean ignoreDaemonThreads,
boolean useEnvVarsFromAppInfo,
@Nullable String fixedApplicationPath,
boolean redirectStdoutStderr,
boolean logJsonToFile,
boolean clearLogHandlers,
Path jsonLogDir) {
this.servletEngine = servletEngine;
this.sandboxPlugin = sandboxPlugin;
this.rpcPlugin = rpcPlugin;
this.requestManager = requestManager;
this.appVersionFactory =
AppVersionFactory.builder()
.setSandboxPlugin(sandboxPlugin)
.setSharedDirectory(sharedDirectory)
.setRuntimeVersion(runtimeVersion)
.setDefaultToNativeUrlStreamHandler(defaultToNativeUrlStreamHandler)
.setForceUrlfetchUrlStreamHandler(forceUrlfetchUrlStreamHandler)
.setIgnoreDaemonThreads(ignoreDaemonThreads)
.setUseEnvVarsFromAppInfo(useEnvVarsFromAppInfo)
.setFixedApplicationPath(fixedApplicationPath)
.build();
this.runtimeVersion = runtimeVersion;
this.templateConfiguration = configuration;
this.deadlineOracle = deadlineOracle;
this.coordinator = coordinator;
this.compressResponse = compressResponse;
this.enableHotspotPerformanceMetrics = enableHotspotPerformanceMetrics;
this.pollForNetwork = pollForNetwork;
this.redirectStdoutStderr = redirectStdoutStderr;
this.logJsonToFile = logJsonToFile;
this.clearLogHandlers = clearLogHandlers;
this.jsonLogDir = jsonLogDir;
}
/**
* Starts the Stubby service, and then perform any initialization that the servlet engine
* requires.
*/
public void start(ServletEngineAdapter.Config runtimeOptions) {
logger.atInfo().log("JavaRuntime starting...");
if (enableHotspotPerformanceMetrics) {
try {
// The Perf class is in different packages in Java 8 and Java 9.
try {
hotspotPerformanceData = getPerformanceDataByteBuffer("sun.misc.Perf");
} catch (ClassNotFoundException e) {
hotspotPerformanceData = getPerformanceDataByteBuffer("jdk.internal.perf.Perf");
}
} catch (Exception e) {
logger.atWarning().withCause(e).log("Failed to access Hotspot performance data");
}
}
SynchronousQueue