Skip to content

Commit 4a2b717

Browse files
authored
Use generated class instead of jar manifest to populate notifier version (#265)
* Use generated class instead of jar manifest to populate notifier version in payload. This fixes the notifier version attribute when users shade rollbar-java, since until now we were reporting the version of the user's application as the notifier version.
1 parent d29533b commit 4a2b717

File tree

4 files changed

+99
-15
lines changed

4 files changed

+99
-15
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,6 @@ atlassian-ide-plugin.xml
7070

7171
# Secrets
7272
.github/secring.gpg
73+
74+
# Local gradle properties
75+
local.properties

rollbar-java/build.gradle

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,65 @@ buildscript {
1212
apply plugin: "nebula.integtest"
1313

1414
dependencies {
15-
api project(':rollbar-api')
15+
api project(':rollbar-api')
1616

17-
api group: 'org.slf4j', name: 'slf4j-api', version: '1.7.25'
17+
api group: 'org.slf4j', name: 'slf4j-api', version: '1.7.25'
1818

19-
compileOnly 'com.google.code.findbugs:jsr305:3.0.2'
19+
compileOnly 'com.google.code.findbugs:jsr305:3.0.2'
2020

21-
integTestImplementation group: 'com.github.tomakehurst', name: 'wiremock', version: '2.27.0'
22-
integTestImplementation group: 'com.google.code.gson', name: 'gson', version: '2.8.2'
21+
integTestImplementation group: 'com.github.tomakehurst', name: 'wiremock', version: '2.27.0'
22+
integTestImplementation group: 'com.google.code.gson', name: 'gson', version: '2.8.2'
23+
}
24+
25+
/**
26+
* This task will create a version property that is statically referenced when populating the
27+
* `notifier` section of the payload. It helps when users shade and / or relocate the
28+
* `rollbar-java` classes, since in those cases we no longer have access to our jar manifest.
29+
* The task creates a Java class instead of a text resource, since dynamically loaded resources
30+
* are not as reliable under relocation as a strongly typed bytecode reference to a compiled class.
31+
*/
32+
task createVersionClass() {
33+
ext.set("OUTPUT_DIR", [buildDir.getAbsolutePath(), 'src', 'generated', 'main'].join(File.separator))
34+
35+
outputs.dir(ext.OUTPUT_DIR)
36+
37+
doLast {
38+
def pkg = ["com", "rollbar", "notifier", "provider", "notifier"];
39+
40+
def pkgName = pkg.join(".");
41+
def pkgPath = "${ext.OUTPUT_DIR}${File.separator}${pkg.join(File.separator)}"
42+
def escapedVersion = VERSION_NAME.replace('\\', '\\\\').replace('"', '\\"');
43+
44+
def classText = """package ${pkgName};
45+
46+
class VersionHelperResources {
47+
static String getVersion() {
48+
return "${escapedVersion}";
49+
}
50+
}
51+
"""
52+
53+
new File(pkgPath).mkdirs()
54+
def classFile = new File(pkgPath, 'VersionHelperResources.java').newWriter()
55+
try {
56+
classFile << classText
57+
} finally {
58+
classFile.close()
59+
}
60+
}
61+
}
62+
63+
sourceSets {
64+
main {
65+
java.srcDirs += project.tasks.createVersionClass.ext.OUTPUT_DIR
66+
}
67+
}
68+
69+
project.tasks.compileJava.dependsOn(project.tasks.createVersionClass);
70+
project.tasks.checkstyleMain.dependsOn(project.tasks.createVersionClass);
71+
72+
test {
73+
// This helps us test the VersionHelper class since there's no jar manifest available when
74+
// running tests.
75+
systemProperty 'ROLLBAR_IMPLEMENTATION_VERSION', VERSION_NAME
2376
}

rollbar-java/src/main/java/com/rollbar/notifier/provider/notifier/VersionHelper.java

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,26 @@
22

33
class VersionHelper {
44

5+
/**
6+
* Get the current version of the `rollbar-java` notifier.
7+
*
8+
* <p>
9+
* When shading `rollbar-java` into a different jar, the version from our jar's manifest is lost,
10+
* and only the user's version is kept. Our classes become part of the user's jar, and
11+
* `VersionHelper.class.getPackage().getImplementationVersion()` returns the user's jar's
12+
* implementation version.
13+
* There several shading tools out there with different levels of support for resources, manifest
14+
* merging, etc... The only thing they all reliably support when they relocate a class is updating
15+
* class and method references present in bytecode form.
16+
* So rather than putting our version in a resource and hoping that we can still dynamically
17+
* reference it after relocation, we just create the VersionHelperResources class in Gradle, which
18+
* we know will still work after relocation since we're referencing it statically.
19+
* Obviously we keep the version in `rollbar-java`'s jar manifest, but we don't rely on it here.
20+
* </p>
21+
*
22+
* @return The version of the `rollbar-java` notifier currently loaded.
23+
*/
524
public String version() {
6-
String version = VersionHelper.class.getPackage().getImplementationVersion();
7-
8-
return version != null ? version : "unknown";
25+
return com.rollbar.notifier.provider.notifier.VersionHelperResources.getVersion();
926
}
1027
}
Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,27 @@
11
package com.rollbar.notifier.provider.notifier;
22

3-
import static org.hamcrest.core.Is.is;
4-
import static org.junit.Assert.assertThat;
5-
63
import org.junit.Test;
74

8-
public class VersionHelperTest {
5+
import static org.hamcrest.Matchers.*;
6+
import static org.junit.Assert.assertThat;
7+
import static org.junit.Assume.assumeThat;
98

9+
public class VersionHelperTest {
1010
@Test
11-
public void shouldReturnNullIfNotAvailable() {
11+
public void shouldReturnVersion() {
1212
VersionHelper helper = new VersionHelper();
13+
// It will fail when we upgrade to 2.x, but it's stable enough. Better than nothing when running
14+
// from an IDE, without the version property that we set in Gradle.
15+
assertThat(helper.version(), startsWith("1."));
16+
}
17+
18+
@Test
19+
public void versionReturnedShouldMatchManifestVersion() {
20+
// We set this in Gradle since there's no jar manifest available when running tests.
21+
String expectedVersion = System.getProperty("ROLLBAR_IMPLEMENTATION_VERSION");
22+
assumeThat(expectedVersion, not(isEmptyOrNullString()));
1323

14-
assertThat(helper.version(), is("unknown"));
24+
VersionHelper helper = new VersionHelper();
25+
assertThat(helper.version(), equalTo(expectedVersion));
1526
}
16-
}
27+
}

0 commit comments

Comments
 (0)