Slightly relax Java Poison Pill on prerelease versions (-rc1, -dev, etc).
Before this, the poison pill intended to require exact match including suffix if there were suffixes on either gencode or runtime.
After this change:
- If gencode is suffixed, it must be exact match to runtime (including suffix).
- If runtime is suffixed and gencode is not suffixed, the gencode must be a strictly lower major.minor.point (4.32.1-rc1 runtime must be on <= 4.32.0 gencode). We log a warning once if this allowed inequality state happens.
This change also fixes a bug where a lower minor version was allowed if it did have the same suffix (4.33.0-rc1 runtime accidentally accepted 4.32.0-rc1 gencode while still rejecting 4.32.0).
PiperOrigin-RevId: 813286207
diff --git a/java/core/src/main/java/com/google/protobuf/RuntimeVersion.java b/java/core/src/main/java/com/google/protobuf/RuntimeVersion.java
index e6cbd81..f9bba40 100644
--- a/java/core/src/main/java/com/google/protobuf/RuntimeVersion.java
+++ b/java/core/src/main/java/com/google/protobuf/RuntimeVersion.java
@@ -46,6 +46,9 @@
@SuppressWarnings("NonFinalStaticField")
static int minorWarningLoggedCount = 0;
+ @SuppressWarnings("NonFinalStaticField")
+ static boolean preleaseRuntimeWarningLogged = false;
+
private static final String VERSION_STRING = versionString(MAJOR, MINOR, PATCH, SUFFIX);
private static final Logger logger = Logger.getLogger(RuntimeVersion.class.getName());
@@ -117,6 +120,26 @@
String gencodeVersionString = null;
+ if (!SUFFIX.isEmpty() && !preleaseRuntimeWarningLogged) {
+ if (gencodeVersionString == null) {
+ gencodeVersionString = versionString(major, minor, patch, suffix);
+ }
+ logger.warning(
+ String.format(
+ Locale.US,
+ " Protobuf prelease version %s in use. This is not recommended for "
+ + "production use.\n"
+ + " You can ignore this message if you are deliberately testing a prerelease."
+ + " Otherwise you should switch to a non-prerelease Protobuf version.",
+ VERSION_STRING));
+ preleaseRuntimeWarningLogged = true;
+ }
+
+ // Exact match is always good.
+ if (major == MAJOR && minor == MINOR && patch == PATCH && suffix.equals(SUFFIX)) {
+ return;
+ }
+
// Check that runtime major version is the same as the gencode major version.
if (major != MAJOR) {
if (major == MAJOR - 1 && majorWarningLoggedCount < MAX_WARNING_COUNT) {
@@ -158,8 +181,15 @@
VERSION_STRING));
}
- // Check that runtime version suffix is the same as the gencode version suffix.
- if (!suffix.equals(SUFFIX)) {
+ // If neither gencode or runtime has a suffix we're done.
+ if (suffix.isEmpty() && SUFFIX.isEmpty()) {
+ return;
+ }
+
+ // If gencode has any suffix, we only support exact matching runtime including suffix. Exact
+ // match including suffix was already checked above so if we get this far and the gencode has a
+ // suffix it is a disallowed combination.
+ if (!suffix.isEmpty()) {
if (gencodeVersionString == null) {
gencodeVersionString = versionString(major, minor, patch, suffix);
}
@@ -167,7 +197,28 @@
String.format(
Locale.US,
"Detected mismatched Protobuf Gencode/Runtime version suffixes when loading %s:"
- + " gencode %s, runtime %s. Version suffixes must be the same.",
+ + " gencode %s, runtime %s. Prerelease gencode must be used with the same"
+ + " runtime.",
+ location,
+ gencodeVersionString,
+ VERSION_STRING));
+ }
+
+ // Here we know the runtime version is suffixed and the gencode version is not. If the
+ // major.minor.patch is exact match, its an illegal combination (eg 4.32.0-rc1 runtime with
+ // 4.32.0 gencode). If major.minor.patch is not an exact match, then the gencode is a lower
+ // version (e.g 4.32.0-rc1 runtime with 4.31.0 gencode) which is allowed.
+ if (major == MAJOR && minor == MINOR && patch == PATCH) {
+ if (gencodeVersionString == null) {
+ gencodeVersionString = versionString(major, minor, patch, suffix);
+ }
+ throw new ProtobufRuntimeVersionException(
+ String.format(
+ Locale.US,
+ "Detected mismatched Protobuf Gencode/Runtime version suffixes when loading %s:"
+ + " gencode %s, runtime %s. Prelease runtimes must only be used with exact match"
+ + " gencode (including suffix) or non-prerelease gencode versions of a"
+ + " lower version.",
location,
gencodeVersionString,
VERSION_STRING));
diff --git a/java/core/src/test/java/com/google/protobuf/RuntimeVersionTest.java b/java/core/src/test/java/com/google/protobuf/RuntimeVersionTest.java
index 0f5edbf..521694b 100644
--- a/java/core/src/test/java/com/google/protobuf/RuntimeVersionTest.java
+++ b/java/core/src/test/java/com/google/protobuf/RuntimeVersionTest.java
@@ -77,18 +77,6 @@
}
@Test
- public void versionValidation_newerRuntimeVersionAllowed() {
- int gencodeMinor = RuntimeVersion.MINOR - 1;
- RuntimeVersion.validateProtobufGencodeVersion(
- RuntimeVersion.DOMAIN,
- RuntimeVersion.MAJOR,
- gencodeMinor,
- RuntimeVersion.PATCH,
- RuntimeVersion.SUFFIX,
- "dummy");
- }
-
- @Test
public void versionValidation_olderRuntimeVersionDisallowed() {
int gencodeMinor = RuntimeVersion.MINOR + 1;
RuntimeVersion.ProtobufRuntimeVersionException thrown =
@@ -147,7 +135,106 @@
}
@Test
+ public void versionValidation_suffixedRuntime_logsWarning() {
+ // We can only execute this test if the test runtime does have a suffix (which is nearly always
+ // for our OSS continuous tests).
+ if (RuntimeVersion.SUFFIX.isEmpty()) {
+ return;
+ }
+
+ // Suffixed runtimes only log the message once. To force the warning to be logged for
+ // this test unrelated to test order, flip the bool back to false if it had been flipped by
+ // another test to ensure the intended log can be observed.
+ RuntimeVersion.preleaseRuntimeWarningLogged = false;
+
+ TestUtil.TestLogHandler logHandler = new TestUtil.TestLogHandler();
+ Logger logger = Logger.getLogger(RuntimeVersion.class.getName());
+ logger.addHandler(logHandler);
+ RuntimeVersion.validateProtobufGencodeVersion(
+ RuntimeVersion.DOMAIN,
+ RuntimeVersion.MAJOR,
+ RuntimeVersion.MINOR,
+ RuntimeVersion.PATCH,
+ RuntimeVersion.SUFFIX,
+ "dummy");
+ assertThat(logHandler.getStoredLogRecords()).hasSize(1);
+ assertThat(logHandler.getStoredLogRecords().get(0).getMessage())
+ .contains("You can ignore this message if you are deliberately testing a prerelease.");
+ }
+
+ @Test
+ public void versionValidation_suffixedRuntime_sameSuffixLowerMinorDisallowed() {
+ // We can only execute this test if the test runtime does have a suffix (which is nearly always
+ // for our OSS continuous tests).
+ if (RuntimeVersion.SUFFIX.isEmpty()) {
+ return;
+ }
+ RuntimeVersion.ProtobufRuntimeVersionException thrown =
+ assertThrows(
+ RuntimeVersion.ProtobufRuntimeVersionException.class,
+ () ->
+ RuntimeVersion.validateProtobufGencodeVersion(
+ RuntimeVersion.DOMAIN,
+ RuntimeVersion.MAJOR,
+ RuntimeVersion.MINOR - 1,
+ RuntimeVersion.PATCH,
+ RuntimeVersion.SUFFIX,
+ "testing.Foo"));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains(
+ "Detected mismatched Protobuf Gencode/Runtime version suffixes when loading"
+ + " testing.Foo");
+ }
+
+ @Test
+ public void versionValidation_suffixedRuntime_sameNumbersNoSuffixDisallowed() {
+ // We can only execute this test if the test runtime does have a suffix (which is nearly always
+ // for our OSS continuous tests).
+ if (RuntimeVersion.SUFFIX.isEmpty()) {
+ return;
+ }
+ RuntimeVersion.ProtobufRuntimeVersionException thrown =
+ assertThrows(
+ RuntimeVersion.ProtobufRuntimeVersionException.class,
+ () ->
+ RuntimeVersion.validateProtobufGencodeVersion(
+ RuntimeVersion.DOMAIN,
+ RuntimeVersion.MAJOR,
+ RuntimeVersion.MINOR,
+ RuntimeVersion.PATCH,
+ "",
+ "testing.Foo"));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains(
+ "Detected mismatched Protobuf Gencode/Runtime version suffixes when loading"
+ + " testing.Foo");
+ }
+
+ @Test
+ public void versionValidation_suffixedRuntime_allowedLowerVersionWarns() {
+ // We can only execute this test if the runtime does have a suffix (which is nearly always for
+ // our OSS continuous tests).
+ if (RuntimeVersion.SUFFIX.isEmpty()) {
+ return;
+ }
+ RuntimeVersion.validateProtobufGencodeVersion(
+ RuntimeVersion.DOMAIN,
+ RuntimeVersion.MAJOR,
+ RuntimeVersion.MINOR - 1,
+ RuntimeVersion.PATCH,
+ "",
+ "testing.Foo");
+ }
+
+ @Test
public void versionValidation_gencodeOneMajorVersionOlderWarning() {
+ // Hack: if this is a suffixed runtime it may log the prerelease warning here if this
+ // is the first test to run. Force the bool to true to avoid the warning happening during
+ // this test only if it was the first one run.
+ RuntimeVersion.preleaseRuntimeWarningLogged = true;
+
TestUtil.TestLogHandler logHandler = new TestUtil.TestLogHandler();
Logger logger = Logger.getLogger(RuntimeVersion.class.getName());
logger.addHandler(logHandler);
@@ -156,7 +243,7 @@
RuntimeVersion.MAJOR - 1,
RuntimeVersion.MINOR,
RuntimeVersion.PATCH,
- RuntimeVersion.SUFFIX,
+ "",
"dummy");
assertThat(logHandler.getStoredLogRecords()).hasSize(1);
assertThat(logHandler.getStoredLogRecords().get(0).getMessage())