From d84e26d80c39e3b8e34c7e8bbef0ffb35e2947b5 Mon Sep 17 00:00:00 2001 From: inder123 Date: Thu, 17 May 2018 09:41:21 -0700 Subject: [PATCH] Issue 1242: Printing Gson version when throwing AssertionError and IllegalArgumentException (#1321) On some versions of Android (probably on some variants of the popular Samsung S4 phone), an older version of Gson is suspected to be bundled in, and gets picked up from the system classpath. For those versions, the applications that include the latest Gson fail unexpectedly. This debug print will help confirm this issue. --- gson/pom.xml | 19 +++- .../google/gson/internal/GsonBuildConfig.java | 30 ++++++ gson/src/main/java/com/google/gson/Gson.java | 9 +- .../GsonVersionDiagnosticsTest.java | 96 +++++++++++++++++++ .../gson/internal/GsonBuildConfigTest.java | 33 +++++++ 5 files changed, 185 insertions(+), 2 deletions(-) create mode 100644 gson/src/main/java-templates/com/google/gson/internal/GsonBuildConfig.java create mode 100644 gson/src/test/java/com/google/gson/functional/GsonVersionDiagnosticsTest.java create mode 100644 gson/src/test/java/com/google/gson/internal/GsonBuildConfigTest.java diff --git a/gson/pom.xml b/gson/pom.xml index 13c2515a..0063f1cb 100644 --- a/gson/pom.xml +++ b/gson/pom.xml @@ -17,7 +17,7 @@ test - + @@ -56,6 +56,23 @@ org.apache.felix maven-bundle-plugin + + org.codehaus.mojo + templating-maven-plugin + 1.0.0 + + + filtering-java-templates + + filter-sources + + + ${basedir}/src/main/java-templates + ${project.build.directory}/generated-sources/java-templates + + + + diff --git a/gson/src/main/java-templates/com/google/gson/internal/GsonBuildConfig.java b/gson/src/main/java-templates/com/google/gson/internal/GsonBuildConfig.java new file mode 100644 index 00000000..73c61546 --- /dev/null +++ b/gson/src/main/java-templates/com/google/gson/internal/GsonBuildConfig.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2018 The Gson authors + * + * 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 + * + * http://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.gson.internal; + +/** + * Build configuration for Gson. This file is automatically populated by + * templating-maven-plugin and .java/.class files are generated for use in Gson. + * + * @author Inderjeet Singh + */ +public final class GsonBuildConfig { + // Based on https://stackoverflow.com/questions/2469922/generate-a-version-java-file-in-maven + + /** This field is automatically populated by Maven when a build is triggered */ + public static final String VERSION = "${project.version}"; +} diff --git a/gson/src/main/java/com/google/gson/Gson.java b/gson/src/main/java/com/google/gson/Gson.java index 5b1c6c83..b19302da 100644 --- a/gson/src/main/java/com/google/gson/Gson.java +++ b/gson/src/main/java/com/google/gson/Gson.java @@ -37,6 +37,7 @@ import java.util.concurrent.atomic.AtomicLongArray; import com.google.gson.internal.ConstructorConstructor; import com.google.gson.internal.Excluder; +import com.google.gson.internal.GsonBuildConfig; import com.google.gson.internal.Primitives; import com.google.gson.internal.Streams; import com.google.gson.internal.bind.ArrayTypeAdapter; @@ -461,7 +462,7 @@ public final class Gson { return candidate; } } - throw new IllegalArgumentException("GSON cannot handle " + type); + throw new IllegalArgumentException("GSON (" + GsonBuildConfig.VERSION + ") cannot handle " + type); } finally { threadCalls.remove(type); @@ -703,6 +704,8 @@ public final class Gson { ((TypeAdapter) adapter).write(writer, src); } catch (IOException e) { throw new JsonIOException(e); + } catch (AssertionError e) { + throw new AssertionError("AssertionError (GSON " + GsonBuildConfig.VERSION + "): " + e.getMessage(), e); } finally { writer.setLenient(oldLenient); writer.setHtmlSafe(oldHtmlSafe); @@ -779,6 +782,8 @@ public final class Gson { Streams.write(jsonElement, writer); } catch (IOException e) { throw new JsonIOException(e); + } catch (AssertionError e) { + throw new AssertionError("AssertionError (GSON " + GsonBuildConfig.VERSION + "): " + e.getMessage(), e); } finally { writer.setLenient(oldLenient); writer.setHtmlSafe(oldHtmlSafe); @@ -935,6 +940,8 @@ public final class Gson { } catch (IOException e) { // TODO(inder): Figure out whether it is indeed right to rethrow this as JsonSyntaxException throw new JsonSyntaxException(e); + } catch (AssertionError e) { + throw new AssertionError("AssertionError (GSON " + GsonBuildConfig.VERSION + "): " + e.getMessage(), e); } finally { reader.setLenient(oldLenient); } diff --git a/gson/src/test/java/com/google/gson/functional/GsonVersionDiagnosticsTest.java b/gson/src/test/java/com/google/gson/functional/GsonVersionDiagnosticsTest.java new file mode 100644 index 00000000..36eff8e1 --- /dev/null +++ b/gson/src/test/java/com/google/gson/functional/GsonVersionDiagnosticsTest.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2018 Gson Authors + * + * 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 + * + * http://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.gson.functional; + +import java.io.IOException; +import java.util.regex.Pattern; + +import org.junit.Before; +import org.junit.Test; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; + +import junit.framework.TestCase; + +/** + * Functional tests to validate printing of Gson version on AssertionErrors + * + * @author Inderjeet Singh + */ +public class GsonVersionDiagnosticsTest extends TestCase { + private static final Pattern GSON_VERSION_PATTERN = Pattern.compile("(\\(GSON \\d\\.\\d\\.\\d)(?:[-.][A-Z]+)?\\)$"); + + private Gson gson; + + @Before + public void setUp() { + gson = new GsonBuilder().registerTypeAdapter(TestType.class, new TypeAdapter() { + @Override public void write(JsonWriter out, TestType value) { + throw new AssertionError("Expected during serialization"); + } + @Override public TestType read(JsonReader in) throws IOException { + throw new AssertionError("Expected during deserialization"); + } + }).create(); + } + + @Test + public void testVersionPattern() { + assertTrue(GSON_VERSION_PATTERN.matcher("(GSON 2.8.5)").matches()); + assertTrue(GSON_VERSION_PATTERN.matcher("(GSON 2.8.5-SNAPSHOT)").matches()); + } + + @Test + public void testAssertionErrorInSerializationPrintsVersion() { + try { + gson.toJson(new TestType()); + fail(); + } catch (AssertionError expected) { + ensureAssertionErrorPrintsGsonVersion(expected); + } + } + + @Test + public void testAssertionErrorInDeserializationPrintsVersion() { + try { + gson.fromJson("{'a':'abc'}", TestType.class); + fail(); + } catch (AssertionError expected) { + ensureAssertionErrorPrintsGsonVersion(expected); + } + } + + private void ensureAssertionErrorPrintsGsonVersion(AssertionError expected) { + String msg = expected.getMessage(); + // System.err.println(msg); + int start = msg.indexOf("(GSON"); + assertTrue(start > 0); + int end = msg.indexOf("):") + 1; + assertTrue(end > 0 && end > start + 6); + String version = msg.substring(start, end); + // System.err.println(version); + assertTrue(GSON_VERSION_PATTERN.matcher(version).matches()); + } + + private static final class TestType { + @SuppressWarnings("unused") + String a; + } +} diff --git a/gson/src/test/java/com/google/gson/internal/GsonBuildConfigTest.java b/gson/src/test/java/com/google/gson/internal/GsonBuildConfigTest.java new file mode 100644 index 00000000..dc39bc02 --- /dev/null +++ b/gson/src/test/java/com/google/gson/internal/GsonBuildConfigTest.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2018 The Gson authors + * + * 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 + * + * http://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.gson.internal; + +import static org.junit.Assert.assertFalse; + +import org.junit.Test; + +/** + * Unit tests for {@code GsonBuildConfig} + * + * @author Inderjeet Singh + */ +public class GsonBuildConfigTest { + + @Test + public void testEnsureGsonBuildConfigGetsUpdatedToMavenVersion() { + assertFalse("${project.version}".equals(GsonBuildConfig.VERSION)); + } +}