1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // 4 // Use of this source code is governed by a BSD-style 5 // license that can be found in the LICENSE file or at 6 // https://developers.google.com/open-source/licenses/bsd 7 8 package com.google.protobuf; 9 10 import java.util.logging.Logger; 11 12 /** 13 * Provides the version of this Protobuf Java runtime, and methods for Protobuf Java gencode to 14 * validate that versions are compatible. Fields and methods in this class should be only accessed 15 * by related unit tests and Protobuf Java gencode, and should not be used elsewhere. 16 */ 17 public final class RuntimeVersion { 18 19 /** Indicates the domain of the Protobuf artifact. */ 20 public enum RuntimeDomain { 21 GOOGLE_INTERNAL, 22 PUBLIC, 23 } 24 25 // The version of this runtime. 26 // Automatically updated by Protobuf release process. Do not edit manually. 27 // These OSS versions are not stripped to avoid merging conflicts. 28 public static final RuntimeDomain OSS_DOMAIN = RuntimeDomain.PUBLIC; 29 public static final int OSS_MAJOR = 4; 30 public static final int OSS_MINOR = 29; 31 public static final int OSS_PATCH = 4; 32 public static final String OSS_SUFFIX = ""; 33 34 public static final RuntimeDomain DOMAIN = OSS_DOMAIN; 35 public static final int MAJOR = OSS_MAJOR; 36 public static final int MINOR = OSS_MINOR; 37 public static final int PATCH = OSS_PATCH; 38 public static final String SUFFIX = OSS_SUFFIX; 39 40 private static final int MAX_WARNING_COUNT = 20; 41 42 @SuppressWarnings("NonFinalStaticField") 43 static int majorWarningLoggedCount = 0; 44 45 @SuppressWarnings("NonFinalStaticField") 46 static int minorWarningLoggedCount = 0; 47 48 private static final String VERSION_STRING = versionString(MAJOR, MINOR, PATCH, SUFFIX); 49 private static final Logger logger = Logger.getLogger(RuntimeVersion.class.getName()); 50 51 /** 52 * Validates that the gencode version is compatible with this runtime version according to 53 * https://protobuf.dev/support/cross-version-runtime-guarantee/. 54 * 55 * <p>This method is currently only used by Protobuf Java **full version** gencode. Do not call it 56 * elsewhere. 57 * 58 * @param domain the domain where Protobuf Java code was generated. 59 * @param major the major version of Protobuf Java gencode. 60 * @param minor the minor version of Protobuf Java gencode. 61 * @param patch the micro/patch version of Protobuf Java gencode. 62 * @param suffix the version suffix e.g. "-rc2", "-dev", etc. 63 * @param location the debugging location e.g. generated Java class to put in the error messages. 64 * @throws ProtobufRuntimeVersionException if versions are incompatible. 65 */ validateProtobufGencodeVersion( RuntimeDomain domain, int major, int minor, int patch, String suffix, String location)66 public static void validateProtobufGencodeVersion( 67 RuntimeDomain domain, int major, int minor, int patch, String suffix, String location) { 68 if (checkDisabled()) { 69 return; 70 } 71 validateProtobufGencodeVersionImpl(domain, major, minor, patch, suffix, location); 72 } 73 74 /** The actual implementation of version validation. */ validateProtobufGencodeVersionImpl( RuntimeDomain domain, int major, int minor, int patch, String suffix, String location)75 private static void validateProtobufGencodeVersionImpl( 76 RuntimeDomain domain, int major, int minor, int patch, String suffix, String location) { 77 if (checkDisabled()) { 78 return; 79 } 80 String gencodeVersionString = versionString(major, minor, patch, suffix); 81 // Check that version numbers are valid. 82 if (major < 0 || minor < 0 || patch < 0) { 83 throw new ProtobufRuntimeVersionException("Invalid gencode version: " + gencodeVersionString); 84 } 85 86 // Check that runtime domain is the same as the gencode domain. 87 if (domain != DOMAIN) { 88 throw new ProtobufRuntimeVersionException( 89 String.format( 90 "Detected mismatched Protobuf Gencode/Runtime domains when loading %s: gencode %s," 91 + " runtime %s. Cross-domain usage of Protobuf is not supported.", 92 location, domain, DOMAIN)); 93 } 94 95 // Check that runtime major version is the same as the gencode major version. 96 if (major != MAJOR) { 97 if (major == MAJOR - 1 && majorWarningLoggedCount < MAX_WARNING_COUNT) { 98 logger.warning( 99 String.format( 100 " Protobuf gencode version %s is exactly one major version older than the runtime" 101 + " version %s at %s. Please update the gencode to avoid compatibility" 102 + " violations in the next runtime release.", 103 gencodeVersionString, VERSION_STRING, location)); 104 majorWarningLoggedCount++; 105 } else { 106 throw new ProtobufRuntimeVersionException( 107 String.format( 108 "Detected mismatched Protobuf Gencode/Runtime major versions when loading %s:" 109 + " gencode %s, runtime %s. Same major version is required.", 110 location, gencodeVersionString, VERSION_STRING)); 111 } 112 } 113 114 // Check that runtime version is newer than the gencode version. 115 if (MINOR < minor || (minor == MINOR && PATCH < patch)) { 116 throw new ProtobufRuntimeVersionException( 117 String.format( 118 "Detected incompatible Protobuf Gencode/Runtime versions when loading %s: gencode %s," 119 + " runtime %s. Runtime version cannot be older than the linked gencode version.", 120 location, gencodeVersionString, VERSION_STRING)); 121 } 122 123 // Check that runtime version suffix is the same as the gencode version suffix. 124 if (!suffix.equals(SUFFIX)) { 125 throw new ProtobufRuntimeVersionException( 126 String.format( 127 "Detected mismatched Protobuf Gencode/Runtime version suffixes when loading %s:" 128 + " gencode %s, runtime %s. Version suffixes must be the same.", 129 location, gencodeVersionString, VERSION_STRING)); 130 } 131 } 132 133 /** 134 * A runtime exception to be thrown by the version validator if version is not well defined or 135 * versions mismatch. 136 */ 137 public static final class ProtobufRuntimeVersionException extends RuntimeException { ProtobufRuntimeVersionException(String message)138 public ProtobufRuntimeVersionException(String message) { 139 super(message); 140 } 141 } 142 143 /** Gets the version string given the version segments. */ versionString(int major, int minor, int patch, String suffix)144 private static String versionString(int major, int minor, int patch, String suffix) { 145 return String.format("%d.%d.%d%s", major, minor, patch, suffix); 146 } 147 checkDisabled()148 private static boolean checkDisabled() { 149 // Check the environmental variable, and temporarily disable validation if it's set to true. 150 String disableFlag = java.lang.System.getenv("TEMPORARILY_DISABLE_PROTOBUF_VERSION_CHECK"); 151 if ((disableFlag != null && disableFlag.equals("true"))) { 152 return true; 153 } 154 155 return false; 156 } 157 RuntimeVersion()158 private RuntimeVersion() {} 159 } 160