• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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