Skip to content

Commit

Permalink
added RxJava1 Finder
Browse files Browse the repository at this point in the history
  • Loading branch information
dhilpipre committed Feb 18, 2021
1 parent c2fe264 commit 9735f12
Show file tree
Hide file tree
Showing 9 changed files with 386 additions and 2 deletions.
30 changes: 30 additions & 0 deletions rxjava1-finder/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@

// Build.gradle generated for instrumentation module rxjava1-finder

apply plugin: 'java'

dependencies {
implementation 'io.reactivex:rxjava:1.1.0'

// New Relic Java Agent dependencies
implementation 'com.newrelic.agent.java:newrelic-agent:6.0.0'
implementation 'com.newrelic.agent.java:newrelic-api:6.0.0'
implementation fileTree(include: ['*.jar'], dir: '../libs')
}

jar {
manifest {
attributes 'Implementation-Title': 'com.newrelic.instrumentation.rxjava1-finder'
attributes 'Implementation-Vendor': 'New Relic'
attributes 'Implementation-Vendor-Id': 'com.newrelic'
attributes 'Implementation-Version': 1.0
}
}

verifyInstrumentation {
// Verifier plugin documentation:
// https://github.com/newrelic/newrelic-gradle-verify-instrumentation
// Example:
// passes 'javax.servlet:servlet-api:[2.2,2.5]'
// exclude 'javax.servlet:servlet-api:2.4.public_draft'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.newrelic.agent.instrumentation.pointcuts.frameworks.rxjava1;

import java.util.Set;

import com.newrelic.agent.deps.org.objectweb.asm.Type;
import com.newrelic.agent.deps.org.objectweb.asm.commons.Method;
import com.newrelic.agent.instrumentation.methodmatchers.MethodMatcher;
import com.newrelic.agent.util.asm.Utils;

public class CompletableReturnMethodMatcher implements MethodMatcher {

private static final String rxClassname = "rx.Completable";

public CompletableReturnMethodMatcher() {
}

@Override
public Method[] getExactMethods() {
return null;
}

@Override
public boolean matches(int access, String name, String desc, Set<String> annotations) {
Type type = Type.getReturnType(desc);
String classname = type.getClassName();
return isCompletable(classname);
}

private boolean isCompletable(String className) {
if(Utils.isPrimitiveType(className) || className.endsWith("[]") || className.startsWith("com.newrelic")) {
return false;
}
if(className.equals(rxClassname)) {
return true;
}

return false;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.newrelic.agent.instrumentation.pointcuts.frameworks.rxjava1;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import com.newrelic.agent.deps.org.objectweb.asm.Type;
import com.newrelic.agent.deps.org.objectweb.asm.commons.Method;
import com.newrelic.agent.instrumentation.methodmatchers.MethodMatcher;
import com.newrelic.agent.util.asm.Utils;

public class ObservableReturnMethodMatcher implements MethodMatcher {

private static final String rxClassname = "rx.Observable";
private static final String CachedObservable = "rx.internal.operators.CachedObservable";
private static final String ScalarSync = "rx.internal.util.ScalarSynchronousObservable";
private static final List<String> classList;

static {
classList = new ArrayList<String>();
classList.add(rxClassname);
classList.add(CachedObservable);
classList.add(ScalarSync);
}

public ObservableReturnMethodMatcher() {
}

@Override
public Method[] getExactMethods() {
return null;
}

@Override
public boolean matches(int access, String name, String desc, Set<String> annotations) {
Type type = Type.getReturnType(desc);
String classname = type.getClassName();
return isObservable(classname);
}

private boolean isObservable(String className) {
if(Utils.isPrimitiveType(className) || className.endsWith("[]") || className.startsWith("com.newrelic")) {
return false;
}
if(classList.contains(className)) {
return true;
}

if(className.startsWith("rx.observables") && className.endsWith("Observable")) {
return true;
}

if(className.startsWith("rx.observables") && className.endsWith("Subject")) {
return true;
}

if(className.startsWith("rx.subjects") && className.endsWith("Subject")) {
return true;
}

return false;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.newrelic.agent.instrumentation.pointcuts.frameworks.rxjava1;

import java.util.Collection;
import java.util.Collections;

import com.newrelic.agent.bridge.AgentBridge;
import com.newrelic.agent.deps.org.objectweb.asm.ClassReader;
import com.newrelic.agent.instrumentation.classmatchers.ClassMatcher;

public class RxJava1ClassMatcher extends ClassMatcher {

@Override
public Collection<String> getClassNames() {
return Collections.emptyList();
}

@Override
public boolean isMatch(Class<?> var1) {
if(var1.isAnnotation()) return false;

Package classPackage = var1.getPackage();
boolean b = !classPackage.getName().startsWith("rx.");
return b;
}

@Override
public boolean isMatch(ClassLoader loader, ClassReader cr) {
if (loader == null) {
loader = AgentBridge.getAgent().getClass().getClassLoader();
}
String className = cr.getClassName();

boolean b = !className.startsWith("rx/");
return b;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package com.newrelic.agent.instrumentation.pointcuts.frameworks.rxjava1;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;

import com.newrelic.agent.instrumentation.ClassTransformerService;
import com.newrelic.agent.instrumentation.PointCutClassTransformer;
import com.newrelic.agent.service.AbstractService;
import com.newrelic.agent.service.ServiceFactory;
import com.newrelic.api.agent.NewRelic;

public class RxJava1LoaderService extends AbstractService {

private ExecutorService executor = null;

public RxJava1LoaderService() {
super("RxJava1LoaderService");
}

@Override
public boolean isEnabled() {
return true;
}

@Override
protected void doStart() throws Exception {
ClassTransformerService classTransformerService = ServiceFactory.getClassTransformerService();
if(classTransformerService != null) {
PointCutClassTransformer classTransformer = classTransformerService.getClassTransformer();
if(classTransformer != null) {
RxJava1PointCut rxjava1Pointcut = new RxJava1PointCut(classTransformer);
boolean b = classTransformerService.addTraceMatcher(rxjava1Pointcut, "RxJava1");
NewRelic.getAgent().getLogger().log(Level.FINE, "Result of adding RxJava1Pointcut is {0}", b);
} else {
NewRelic.getAgent().getLogger().log(Level.FINE, "Could not load matcher because ClassTransformer is null");
startExecutor();

}
} else {
NewRelic.getAgent().getLogger().log(Level.FINE, "Could not load matcher because ClassTransformerService is null");
startExecutor();
}
}

@Override
protected void doStop() throws Exception {

}

private boolean addTraceMatcher(ClassTransformerService classTransformerService) {
PointCutClassTransformer classTransformer = classTransformerService.getClassTransformer();
RxJava1PointCut rxjava1Pointcut = new RxJava1PointCut(classTransformer);
return classTransformerService.addTraceMatcher(rxjava1Pointcut, "RxJava1");
}

private void startExecutor() {
executor = Executors.newSingleThreadExecutor();
RunCheck runCheck = new RunCheck();
executor.submit(runCheck);
NewRelic.getAgent().getLogger().log(Level.FINE, "Submit RunCheck to executor");
}

private void shutdownExecutor() {
executor.shutdown();
NewRelic.getAgent().getLogger().log(Level.FINE, "ReactorLoaderService executor has shut down");
}


private class RunCheck implements Runnable {

@Override
public void run() {
boolean done = false;
while(!done) {
ClassTransformerService classTransformerService = ServiceFactory.getClassTransformerService();
if(classTransformerService != null) {
PointCutClassTransformer classTransformer = classTransformerService.getClassTransformer();
if(classTransformer != null) {
done = true;
boolean b = addTraceMatcher(classTransformerService);
NewRelic.getAgent().getLogger().log(Level.FINE, "Result of adding ReactorPointcut is {0}", b);
}
} else {
try {
Thread.sleep(5000L);
} catch (InterruptedException e) {
}
}
}
shutdownExecutor();
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.newrelic.agent.instrumentation.pointcuts.frameworks.rxjava1;

import com.newrelic.agent.MetricNames;
import com.newrelic.agent.Transaction;
import com.newrelic.agent.instrumentation.PointCutClassTransformer;
import com.newrelic.agent.instrumentation.PointCutConfiguration;
import com.newrelic.agent.instrumentation.TracerFactoryPointCut;
import com.newrelic.agent.tracers.ClassMethodSignature;
import com.newrelic.agent.tracers.OtherRootTracer;
import com.newrelic.agent.tracers.Tracer;
import com.newrelic.agent.tracers.metricname.ClassMethodMetricNameFormat;

public class RxJava1PointCut extends TracerFactoryPointCut {

public RxJava1PointCut(PointCutClassTransformer classTransformer) {
super(new PointCutConfiguration("rxjava1"),new RxJava1ClassMatcher(), new RxJava1ReturnMethodMatcher());
}



@Override
public boolean isDispatcher() {
return true;
}

@Override
protected Tracer doGetTracer(Transaction transaction, ClassMethodSignature sig, Object rx, Object[] args) {
return new RxJava1MethodTracer(transaction, sig, rx);
}

private static class RxJava1MethodTracer extends OtherRootTracer {

public RxJava1MethodTracer(Transaction transaction, ClassMethodSignature sig, Object rx) {
super(transaction,sig,rx, new ClassMethodMetricNameFormat(sig, rx, MetricNames.OTHER_TRANSACTION+"/RxJava1"));
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.newrelic.agent.instrumentation.pointcuts.frameworks.rxjava1;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import com.newrelic.agent.instrumentation.methodmatchers.ManyMethodMatcher;
import com.newrelic.agent.instrumentation.methodmatchers.MethodMatcher;

public class RxJava1ReturnMethodMatcher extends ManyMethodMatcher {

private static CompletableReturnMethodMatcher completableMatcher = new CompletableReturnMethodMatcher();
private static ObservableReturnMethodMatcher observableMatcher = new ObservableReturnMethodMatcher();
private static SingleReturnMethodMatcher singleMatcher = new SingleReturnMethodMatcher();

private static List<MethodMatcher> matchers;

static {
matchers = new ArrayList<MethodMatcher>();
matchers.add(completableMatcher);
matchers.add(observableMatcher);
matchers.add(singleMatcher);
}

public RxJava1ReturnMethodMatcher() {
super(matchers);
}

@Override
public boolean matches(int access, String name, String desc, Set<String> annotations) {
for(MethodMatcher matcher : matchers) {
if(matcher.matches(access, name, desc, annotations)) {
return true;
}
}
return false;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.newrelic.agent.instrumentation.pointcuts.frameworks.rxjava1;

import java.util.Set;

import com.newrelic.agent.deps.org.objectweb.asm.Type;
import com.newrelic.agent.deps.org.objectweb.asm.commons.Method;
import com.newrelic.agent.instrumentation.methodmatchers.MethodMatcher;
import com.newrelic.agent.util.asm.Utils;

public class SingleReturnMethodMatcher implements MethodMatcher {

private static final String rxClassname = "rx.Single";

public SingleReturnMethodMatcher() {
}

@Override
public Method[] getExactMethods() {
return null;
}

@Override
public boolean matches(int access, String name, String desc, Set<String> annotations) {
Type type = Type.getReturnType(desc);
String classname = type.getClassName();
return isSingle(classname);
}

private boolean isSingle(String className) {
if(Utils.isPrimitiveType(className) || className.endsWith("[]") || className.startsWith("com.newrelic")) {
return false;
}
if(className.equals(rxClassname)) {
return true;
}

return false;
}

}
Loading

0 comments on commit 9735f12

Please sign in to comment.