19
19
package co .elastic .apm .agent .sdk .bytebuddy ;
20
20
21
21
import co .elastic .apm .agent .sdk .internal .InternalUtil ;
22
+ import co .elastic .apm .agent .sdk .internal .util .PrivilegedActionUtils ;
22
23
import co .elastic .apm .agent .sdk .logging .Logger ;
23
24
import co .elastic .apm .agent .sdk .logging .LoggerFactory ;
24
- import co .elastic .apm .agent .sdk .internal .util .PrivilegedActionUtils ;
25
25
import co .elastic .apm .agent .sdk .weakconcurrent .WeakConcurrent ;
26
26
import co .elastic .apm .agent .sdk .weakconcurrent .WeakMap ;
27
27
import net .bytebuddy .description .NamedElement ;
31
31
import javax .annotation .Nullable ;
32
32
import java .io .File ;
33
33
import java .io .IOException ;
34
+ import java .io .InputStream ;
34
35
import java .net .JarURLConnection ;
35
36
import java .net .URISyntaxException ;
36
37
import java .net .URL ;
37
38
import java .net .URLConnection ;
38
39
import java .security .CodeSource ;
39
40
import java .security .ProtectionDomain ;
40
41
import java .util .Collection ;
42
+ import java .util .Properties ;
41
43
import java .util .jar .Attributes ;
42
44
import java .util .jar .JarFile ;
43
45
import java .util .jar .Manifest ;
46
+ import java .util .zip .ZipEntry ;
44
47
45
- import static net .bytebuddy .matcher .ElementMatchers .nameContains ;
46
- import static net .bytebuddy .matcher .ElementMatchers .nameStartsWith ;
47
- import static net .bytebuddy .matcher .ElementMatchers .none ;
48
+ import static net .bytebuddy .matcher .ElementMatchers .*;
48
49
49
50
public class CustomElementMatchers {
50
51
@@ -183,15 +184,23 @@ public static <T extends NamedElement> ElementMatcher.Junction<T> isProxy() {
183
184
* @param version the version to check against
184
185
* @return an LTE SemVer matcher
185
186
*/
186
- public static ElementMatcher .Junction <ProtectionDomain > implementationVersionLte (final String version ) {
187
- return implementationVersion (version , Matcher .LTE );
187
+ public static ElementMatcher .Junction <ProtectionDomain > implementationVersionLte (String version ) {
188
+ return implementationVersion (version , Matcher .LTE , null , null );
188
189
}
189
190
190
- public static ElementMatcher .Junction <ProtectionDomain > implementationVersionGte (final String version ) {
191
- return implementationVersion (version , Matcher .GTE );
191
+ public static ElementMatcher .Junction <ProtectionDomain > implementationVersionGte (String version ) {
192
+ return implementationVersion (version , Matcher .GTE , null , null );
192
193
}
193
194
194
- private static ElementMatcher .Junction <ProtectionDomain > implementationVersion (final String version , final Matcher matcher ) {
195
+ public static ElementMatcher .Junction <ProtectionDomain > implementationVersionLte (String version , String groupId , String artifactId ) {
196
+ return implementationVersion (version , Matcher .LTE , groupId , artifactId );
197
+ }
198
+
199
+ public static ElementMatcher .Junction <ProtectionDomain > implementationVersionGte (String version , String groupId , String artifactId ) {
200
+ return implementationVersion (version , Matcher .GTE , groupId , artifactId );
201
+ }
202
+
203
+ private static ElementMatcher .Junction <ProtectionDomain > implementationVersion (final String version , final Matcher matcher , @ Nullable final String mavenGroupId , @ Nullable final String mavenArtifactId ) {
195
204
return new ElementMatcher .Junction .AbstractBase <ProtectionDomain >() {
196
205
/**
197
206
* Returns true if the implementation version read from the manifest file referenced by the given
@@ -205,7 +214,7 @@ private static ElementMatcher.Junction<ProtectionDomain> implementationVersion(f
205
214
@ Override
206
215
public boolean matches (@ Nullable ProtectionDomain protectionDomain ) {
207
216
try {
208
- Version pdVersion = readImplementationVersionFromManifest (protectionDomain );
217
+ Version pdVersion = readImplementationVersion (protectionDomain , mavenGroupId , mavenArtifactId );
209
218
if (pdVersion != null ) {
210
219
Version limitVersion = Version .of (version );
211
220
return matcher .match (pdVersion , limitVersion );
@@ -221,22 +230,51 @@ public boolean matches(@Nullable ProtectionDomain protectionDomain) {
221
230
}
222
231
223
232
@ Nullable
224
- private static Version readImplementationVersionFromManifest (@ Nullable ProtectionDomain protectionDomain ) throws IOException , URISyntaxException {
233
+ private static Version readImplementationVersion (@ Nullable ProtectionDomain protectionDomain , @ Nullable String mavenGroupId , @ Nullable String mavenArtifactId ) throws IOException , URISyntaxException {
225
234
Version version = null ;
226
235
JarFile jarFile = null ;
236
+
237
+ if (protectionDomain == null ) {
238
+ logger .info ("Cannot read implementation version - got null ProtectionDomain" );
239
+ return null ;
240
+ }
241
+
227
242
try {
228
- if (protectionDomain != null ) {
229
- CodeSource codeSource = protectionDomain .getCodeSource ();
230
- if (codeSource != null ) {
231
- URL jarUrl = codeSource .getLocation ();
232
- if (jarUrl != null ) {
233
- // does not yet establish an actual connection
234
- URLConnection urlConnection = jarUrl .openConnection ();
235
- if (urlConnection instanceof JarURLConnection ) {
236
- jarFile = ((JarURLConnection ) urlConnection ).getJarFile ();
237
- } else {
238
- jarFile = new JarFile (new File (jarUrl .toURI ()));
243
+ CodeSource codeSource = protectionDomain .getCodeSource ();
244
+ if (codeSource != null ) {
245
+ URL jarUrl = codeSource .getLocation ();
246
+ if (jarUrl != null ) {
247
+ // does not yet establish an actual connection
248
+ URLConnection urlConnection = jarUrl .openConnection ();
249
+ if (urlConnection instanceof JarURLConnection ) {
250
+ jarFile = ((JarURLConnection ) urlConnection ).getJarFile ();
251
+ } else {
252
+ jarFile = new JarFile (new File (jarUrl .toURI ()));
253
+ }
254
+
255
+ // read maven properties first as they have higher priority over manifest
256
+ // this is mostly for shaded libraries in "fat-jar" applications
257
+ if (mavenGroupId != null && mavenArtifactId != null ) {
258
+ ZipEntry zipEntry = jarFile .getEntry (String .format ("META-INF/maven/%s/%s/pom.properties" , mavenGroupId , mavenArtifactId ));
259
+ if (zipEntry != null ) {
260
+ Properties properties = new Properties ();
261
+ try (InputStream input = jarFile .getInputStream (zipEntry )) {
262
+ properties .load (input );
263
+ }
264
+ if (mavenGroupId .equals (properties .getProperty ("groupId" )) && mavenArtifactId .equals (properties .getProperty ("artifactId" ))) {
265
+ String stringVersion = properties .getProperty ("version" );
266
+ if (stringVersion != null ) {
267
+ version = Version .of (stringVersion );
268
+ }
269
+ }
239
270
}
271
+ }
272
+
273
+ // reading manifest if library packaged as a jar
274
+ //
275
+ // doing this after maven properties is important as it might report executable jar version
276
+ // when application is packaged as a "fat jar"
277
+ if (version == null ) {
240
278
Manifest manifest = jarFile .getManifest ();
241
279
if (manifest != null ) {
242
280
Attributes attributes = manifest .getMainAttributes ();
@@ -248,12 +286,9 @@ private static Version readImplementationVersionFromManifest(@Nullable Protectio
248
286
if (manifestVersion != null ) {
249
287
version = Version .of (manifestVersion );
250
288
}
251
-
252
289
}
253
290
}
254
291
}
255
- } else {
256
- logger .info ("Cannot read implementation version - got null ProtectionDomain" );
257
292
}
258
293
} finally {
259
294
if (jarFile != null ) {
0 commit comments