Skip to content

Commit 9735f12

Browse files
author
=
committed
added RxJava1 Finder
1 parent c2fe264 commit 9735f12

9 files changed

Lines changed: 386 additions & 2 deletions

File tree

rxjava1-finder/build.gradle

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
2+
// Build.gradle generated for instrumentation module rxjava1-finder
3+
4+
apply plugin: 'java'
5+
6+
dependencies {
7+
implementation 'io.reactivex:rxjava:1.1.0'
8+
9+
// New Relic Java Agent dependencies
10+
implementation 'com.newrelic.agent.java:newrelic-agent:6.0.0'
11+
implementation 'com.newrelic.agent.java:newrelic-api:6.0.0'
12+
implementation fileTree(include: ['*.jar'], dir: '../libs')
13+
}
14+
15+
jar {
16+
manifest {
17+
attributes 'Implementation-Title': 'com.newrelic.instrumentation.rxjava1-finder'
18+
attributes 'Implementation-Vendor': 'New Relic'
19+
attributes 'Implementation-Vendor-Id': 'com.newrelic'
20+
attributes 'Implementation-Version': 1.0
21+
}
22+
}
23+
24+
verifyInstrumentation {
25+
// Verifier plugin documentation:
26+
// https://github.com/newrelic/newrelic-gradle-verify-instrumentation
27+
// Example:
28+
// passes 'javax.servlet:servlet-api:[2.2,2.5]'
29+
// exclude 'javax.servlet:servlet-api:2.4.public_draft'
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.newrelic.agent.instrumentation.pointcuts.frameworks.rxjava1;
2+
3+
import java.util.Set;
4+
5+
import com.newrelic.agent.deps.org.objectweb.asm.Type;
6+
import com.newrelic.agent.deps.org.objectweb.asm.commons.Method;
7+
import com.newrelic.agent.instrumentation.methodmatchers.MethodMatcher;
8+
import com.newrelic.agent.util.asm.Utils;
9+
10+
public class CompletableReturnMethodMatcher implements MethodMatcher {
11+
12+
private static final String rxClassname = "rx.Completable";
13+
14+
public CompletableReturnMethodMatcher() {
15+
}
16+
17+
@Override
18+
public Method[] getExactMethods() {
19+
return null;
20+
}
21+
22+
@Override
23+
public boolean matches(int access, String name, String desc, Set<String> annotations) {
24+
Type type = Type.getReturnType(desc);
25+
String classname = type.getClassName();
26+
return isCompletable(classname);
27+
}
28+
29+
private boolean isCompletable(String className) {
30+
if(Utils.isPrimitiveType(className) || className.endsWith("[]") || className.startsWith("com.newrelic")) {
31+
return false;
32+
}
33+
if(className.equals(rxClassname)) {
34+
return true;
35+
}
36+
37+
return false;
38+
}
39+
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package com.newrelic.agent.instrumentation.pointcuts.frameworks.rxjava1;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
import java.util.Set;
6+
7+
import com.newrelic.agent.deps.org.objectweb.asm.Type;
8+
import com.newrelic.agent.deps.org.objectweb.asm.commons.Method;
9+
import com.newrelic.agent.instrumentation.methodmatchers.MethodMatcher;
10+
import com.newrelic.agent.util.asm.Utils;
11+
12+
public class ObservableReturnMethodMatcher implements MethodMatcher {
13+
14+
private static final String rxClassname = "rx.Observable";
15+
private static final String CachedObservable = "rx.internal.operators.CachedObservable";
16+
private static final String ScalarSync = "rx.internal.util.ScalarSynchronousObservable";
17+
private static final List<String> classList;
18+
19+
static {
20+
classList = new ArrayList<String>();
21+
classList.add(rxClassname);
22+
classList.add(CachedObservable);
23+
classList.add(ScalarSync);
24+
}
25+
26+
public ObservableReturnMethodMatcher() {
27+
}
28+
29+
@Override
30+
public Method[] getExactMethods() {
31+
return null;
32+
}
33+
34+
@Override
35+
public boolean matches(int access, String name, String desc, Set<String> annotations) {
36+
Type type = Type.getReturnType(desc);
37+
String classname = type.getClassName();
38+
return isObservable(classname);
39+
}
40+
41+
private boolean isObservable(String className) {
42+
if(Utils.isPrimitiveType(className) || className.endsWith("[]") || className.startsWith("com.newrelic")) {
43+
return false;
44+
}
45+
if(classList.contains(className)) {
46+
return true;
47+
}
48+
49+
if(className.startsWith("rx.observables") && className.endsWith("Observable")) {
50+
return true;
51+
}
52+
53+
if(className.startsWith("rx.observables") && className.endsWith("Subject")) {
54+
return true;
55+
}
56+
57+
if(className.startsWith("rx.subjects") && className.endsWith("Subject")) {
58+
return true;
59+
}
60+
61+
return false;
62+
}
63+
64+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.newrelic.agent.instrumentation.pointcuts.frameworks.rxjava1;
2+
3+
import java.util.Collection;
4+
import java.util.Collections;
5+
6+
import com.newrelic.agent.bridge.AgentBridge;
7+
import com.newrelic.agent.deps.org.objectweb.asm.ClassReader;
8+
import com.newrelic.agent.instrumentation.classmatchers.ClassMatcher;
9+
10+
public class RxJava1ClassMatcher extends ClassMatcher {
11+
12+
@Override
13+
public Collection<String> getClassNames() {
14+
return Collections.emptyList();
15+
}
16+
17+
@Override
18+
public boolean isMatch(Class<?> var1) {
19+
if(var1.isAnnotation()) return false;
20+
21+
Package classPackage = var1.getPackage();
22+
boolean b = !classPackage.getName().startsWith("rx.");
23+
return b;
24+
}
25+
26+
@Override
27+
public boolean isMatch(ClassLoader loader, ClassReader cr) {
28+
if (loader == null) {
29+
loader = AgentBridge.getAgent().getClass().getClassLoader();
30+
}
31+
String className = cr.getClassName();
32+
33+
boolean b = !className.startsWith("rx/");
34+
return b;
35+
}
36+
37+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package com.newrelic.agent.instrumentation.pointcuts.frameworks.rxjava1;
2+
3+
import java.util.concurrent.ExecutorService;
4+
import java.util.concurrent.Executors;
5+
import java.util.logging.Level;
6+
7+
import com.newrelic.agent.instrumentation.ClassTransformerService;
8+
import com.newrelic.agent.instrumentation.PointCutClassTransformer;
9+
import com.newrelic.agent.service.AbstractService;
10+
import com.newrelic.agent.service.ServiceFactory;
11+
import com.newrelic.api.agent.NewRelic;
12+
13+
public class RxJava1LoaderService extends AbstractService {
14+
15+
private ExecutorService executor = null;
16+
17+
public RxJava1LoaderService() {
18+
super("RxJava1LoaderService");
19+
}
20+
21+
@Override
22+
public boolean isEnabled() {
23+
return true;
24+
}
25+
26+
@Override
27+
protected void doStart() throws Exception {
28+
ClassTransformerService classTransformerService = ServiceFactory.getClassTransformerService();
29+
if(classTransformerService != null) {
30+
PointCutClassTransformer classTransformer = classTransformerService.getClassTransformer();
31+
if(classTransformer != null) {
32+
RxJava1PointCut rxjava1Pointcut = new RxJava1PointCut(classTransformer);
33+
boolean b = classTransformerService.addTraceMatcher(rxjava1Pointcut, "RxJava1");
34+
NewRelic.getAgent().getLogger().log(Level.FINE, "Result of adding RxJava1Pointcut is {0}", b);
35+
} else {
36+
NewRelic.getAgent().getLogger().log(Level.FINE, "Could not load matcher because ClassTransformer is null");
37+
startExecutor();
38+
39+
}
40+
} else {
41+
NewRelic.getAgent().getLogger().log(Level.FINE, "Could not load matcher because ClassTransformerService is null");
42+
startExecutor();
43+
}
44+
}
45+
46+
@Override
47+
protected void doStop() throws Exception {
48+
49+
}
50+
51+
private boolean addTraceMatcher(ClassTransformerService classTransformerService) {
52+
PointCutClassTransformer classTransformer = classTransformerService.getClassTransformer();
53+
RxJava1PointCut rxjava1Pointcut = new RxJava1PointCut(classTransformer);
54+
return classTransformerService.addTraceMatcher(rxjava1Pointcut, "RxJava1");
55+
}
56+
57+
private void startExecutor() {
58+
executor = Executors.newSingleThreadExecutor();
59+
RunCheck runCheck = new RunCheck();
60+
executor.submit(runCheck);
61+
NewRelic.getAgent().getLogger().log(Level.FINE, "Submit RunCheck to executor");
62+
}
63+
64+
private void shutdownExecutor() {
65+
executor.shutdown();
66+
NewRelic.getAgent().getLogger().log(Level.FINE, "ReactorLoaderService executor has shut down");
67+
}
68+
69+
70+
private class RunCheck implements Runnable {
71+
72+
@Override
73+
public void run() {
74+
boolean done = false;
75+
while(!done) {
76+
ClassTransformerService classTransformerService = ServiceFactory.getClassTransformerService();
77+
if(classTransformerService != null) {
78+
PointCutClassTransformer classTransformer = classTransformerService.getClassTransformer();
79+
if(classTransformer != null) {
80+
done = true;
81+
boolean b = addTraceMatcher(classTransformerService);
82+
NewRelic.getAgent().getLogger().log(Level.FINE, "Result of adding ReactorPointcut is {0}", b);
83+
}
84+
} else {
85+
try {
86+
Thread.sleep(5000L);
87+
} catch (InterruptedException e) {
88+
}
89+
}
90+
}
91+
shutdownExecutor();
92+
}
93+
94+
}
95+
96+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.newrelic.agent.instrumentation.pointcuts.frameworks.rxjava1;
2+
3+
import com.newrelic.agent.MetricNames;
4+
import com.newrelic.agent.Transaction;
5+
import com.newrelic.agent.instrumentation.PointCutClassTransformer;
6+
import com.newrelic.agent.instrumentation.PointCutConfiguration;
7+
import com.newrelic.agent.instrumentation.TracerFactoryPointCut;
8+
import com.newrelic.agent.tracers.ClassMethodSignature;
9+
import com.newrelic.agent.tracers.OtherRootTracer;
10+
import com.newrelic.agent.tracers.Tracer;
11+
import com.newrelic.agent.tracers.metricname.ClassMethodMetricNameFormat;
12+
13+
public class RxJava1PointCut extends TracerFactoryPointCut {
14+
15+
public RxJava1PointCut(PointCutClassTransformer classTransformer) {
16+
super(new PointCutConfiguration("rxjava1"),new RxJava1ClassMatcher(), new RxJava1ReturnMethodMatcher());
17+
}
18+
19+
20+
21+
@Override
22+
public boolean isDispatcher() {
23+
return true;
24+
}
25+
26+
@Override
27+
protected Tracer doGetTracer(Transaction transaction, ClassMethodSignature sig, Object rx, Object[] args) {
28+
return new RxJava1MethodTracer(transaction, sig, rx);
29+
}
30+
31+
private static class RxJava1MethodTracer extends OtherRootTracer {
32+
33+
public RxJava1MethodTracer(Transaction transaction, ClassMethodSignature sig, Object rx) {
34+
super(transaction,sig,rx, new ClassMethodMetricNameFormat(sig, rx, MetricNames.OTHER_TRANSACTION+"/RxJava1"));
35+
}
36+
37+
}
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.newrelic.agent.instrumentation.pointcuts.frameworks.rxjava1;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
import java.util.Set;
6+
7+
import com.newrelic.agent.instrumentation.methodmatchers.ManyMethodMatcher;
8+
import com.newrelic.agent.instrumentation.methodmatchers.MethodMatcher;
9+
10+
public class RxJava1ReturnMethodMatcher extends ManyMethodMatcher {
11+
12+
private static CompletableReturnMethodMatcher completableMatcher = new CompletableReturnMethodMatcher();
13+
private static ObservableReturnMethodMatcher observableMatcher = new ObservableReturnMethodMatcher();
14+
private static SingleReturnMethodMatcher singleMatcher = new SingleReturnMethodMatcher();
15+
16+
private static List<MethodMatcher> matchers;
17+
18+
static {
19+
matchers = new ArrayList<MethodMatcher>();
20+
matchers.add(completableMatcher);
21+
matchers.add(observableMatcher);
22+
matchers.add(singleMatcher);
23+
}
24+
25+
public RxJava1ReturnMethodMatcher() {
26+
super(matchers);
27+
}
28+
29+
@Override
30+
public boolean matches(int access, String name, String desc, Set<String> annotations) {
31+
for(MethodMatcher matcher : matchers) {
32+
if(matcher.matches(access, name, desc, annotations)) {
33+
return true;
34+
}
35+
}
36+
return false;
37+
}
38+
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.newrelic.agent.instrumentation.pointcuts.frameworks.rxjava1;
2+
3+
import java.util.Set;
4+
5+
import com.newrelic.agent.deps.org.objectweb.asm.Type;
6+
import com.newrelic.agent.deps.org.objectweb.asm.commons.Method;
7+
import com.newrelic.agent.instrumentation.methodmatchers.MethodMatcher;
8+
import com.newrelic.agent.util.asm.Utils;
9+
10+
public class SingleReturnMethodMatcher implements MethodMatcher {
11+
12+
private static final String rxClassname = "rx.Single";
13+
14+
public SingleReturnMethodMatcher() {
15+
}
16+
17+
@Override
18+
public Method[] getExactMethods() {
19+
return null;
20+
}
21+
22+
@Override
23+
public boolean matches(int access, String name, String desc, Set<String> annotations) {
24+
Type type = Type.getReturnType(desc);
25+
String classname = type.getClassName();
26+
return isSingle(classname);
27+
}
28+
29+
private boolean isSingle(String className) {
30+
if(Utils.isPrimitiveType(className) || className.endsWith("[]") || className.startsWith("com.newrelic")) {
31+
return false;
32+
}
33+
if(className.equals(rxClassname)) {
34+
return true;
35+
}
36+
37+
return false;
38+
}
39+
40+
}

0 commit comments

Comments
 (0)