FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

WIP - Bytecode based data fetchers by bbakerman · Pull Request #2984 · graphql-java/graphql-java · GitHub

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension .gradle  (1) .groovy  (1) .java  (15) All 3 file types selected
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace
1 change: 1 addition & 0 deletions build.gradle
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ dependencies {
api 'org.reactivestreams:reactive-streams:' + reactiveStreamsVersion
antlr 'org.antlr:antlr4:' + antlrVersion
implementation 'com.google.guava:guava:31.0.1-jre'
implementation('org.javassist:javassist:3.29.2-GA')
testImplementation group: 'junit', name: 'junit', version: '4.13.2'
testImplementation 'org.spockframework:spock-core:2.0-groovy-3.0'
testImplementation 'org.codehaus.groovy:groovy:3.0.9'
Expand Down
109 changes: 98 additions & 11 deletions src/main/java/graphql/schema/PropertyFetchingImpl.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import graphql.GraphQLException;
import graphql.Internal;
import graphql.schema.bytecode.ByteCodePojoFetchingGenerator;
import org.slf4j.Logger;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
Expand All @@ -12,6 +14,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.StringJoiner;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicBoolean;
Expand All @@ -21,15 +24,19 @@
import static graphql.Scalars.GraphQLBoolean;
import static graphql.schema.GraphQLTypeUtil.isNonNull;
import static graphql.schema.GraphQLTypeUtil.unwrapOne;
import static org.slf4j.LoggerFactory.getLogger;

/**
* A re-usable class that can fetch from POJOs
*/
@Internal
public class PropertyFetchingImpl {

private static final Logger log = getLogger(PropertyFetchingImpl.class);

private final AtomicBoolean USE_SET_ACCESSIBLE = new AtomicBoolean(true);
private final AtomicBoolean USE_NEGATIVE_CACHE = new AtomicBoolean(true);
private final ConcurrentMap<GenClassCacheKey, ByteCodePojoFetchingGenerator.Result> GENERATED_CACHE = new ConcurrentHashMap<>();
private final ConcurrentMap<CacheKey, CachedMethod> METHOD_CACHE = new ConcurrentHashMap<>();
private final ConcurrentMap<CacheKey, Field> FIELD_CACHE = new ConcurrentHashMap<>();
private final ConcurrentMap<CacheKey, CacheKey> NEGATIVE_CACHE = new ConcurrentHashMap<>();
Expand All @@ -56,8 +63,23 @@ public Object getPropertyValue(String propertyName, Object object, GraphQLType g
}

CacheKey cacheKey = mkCacheKey(object, propertyName);
// lets try positive cache mechanisms first. If we have seen the method or field before
GenClassCacheKey genClassCacheKey = mkGenCacheKey(object);
// let's try positive cache mechanisms first. If we have seen the method or field before
// then we invoke it directly without burning any cycles doing reflection.

ByteCodePojoFetchingGenerator.Result generatedFetcher = GENERATED_CACHE.get(genClassCacheKey);
boolean performGeneration = true;
if (generatedFetcher != null) {
if (generatedFetcher.handlesProperty(propertyName)) {
try {
return generatedFetcher.getFetcher().fetch(object, propertyName);
} catch (Exception e) {
// we don't expect this but let's go to the old way
}
}
performGeneration = false;
}

CachedMethod cachedMethod = METHOD_CACHE.get(cacheKey);
if (cachedMethod != null) {
try {
Expand All @@ -84,7 +106,29 @@ public Object getPropertyValue(String propertyName, Object object, GraphQLType g
return null;
}
//
// ok we haven't cached it and we haven't negatively cached it so we have to find the POJO method which is the most
// we can try to generate a dynamic class for the object which can prove faster
if (performGeneration) {
ByteCodePojoFetchingGenerator.Result result = null;
try {
result = ByteCodePojoFetchingGenerator.generateClassFor(object.getClass());
GENERATED_CACHE.put(genClassCacheKey, result);
} catch (Exception e) {
// We might not have enough
log.debug("Unable to generate a dynamic class for {}", object.getClass(), e);
}
if (result != null) {
if (result.handlesProperty(propertyName)) {
try {
return result.getFetcher().fetch(object, propertyName);
} catch (Exception e) {
// ok ignore it - we will use the old way
}
}
}
}

//
// ok we haven't cached it, and we haven't negatively cached it, so we have to find the POJO method which is the most
// expensive operation here
//
boolean dfeInUse = singleArgumentValue != null;
Expand Down Expand Up @@ -272,6 +316,7 @@ private boolean isBooleanProperty(GraphQLType graphQLType) {
}

public void clearReflectionCache() {
GENERATED_CACHE.clear();
METHOD_CACHE.clear();
FIELD_CACHE.clear();
NEGATIVE_CACHE.clear();
Expand All @@ -287,8 +332,47 @@ public boolean setUseNegativeCache(boolean flag) {

private CacheKey mkCacheKey(Object object, String propertyName) {
Class<?> clazz = object.getClass();
ClassLoader classLoader = clazz.getClassLoader();
return new CacheKey(classLoader, clazz.getName(), propertyName);
return new CacheKey(clazz.getClassLoader(), clazz.getName(), propertyName);
}

private GenClassCacheKey mkGenCacheKey(Object object) {
Class<?> clazz = object.getClass();
return new GenClassCacheKey(clazz.getClassLoader(), clazz.getName());
}

public static final class GenClassCacheKey {
private final ClassLoader classLoader;
private final String className;

private GenClassCacheKey(ClassLoader classLoader, String className) {
this.classLoader = classLoader;
this.className = className;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
GenClassCacheKey that = (GenClassCacheKey) o;
return classLoader.equals(that.classLoader) && className.equals(that.className);
}

@Override
public int hashCode() {
return Objects.hash(classLoader, className);
}

@Override
public String toString() {
return new StringJoiner(", ", GenClassCacheKey.class.getSimpleName() + "[", "]")
.add("classLoader=" + classLoader)
.add("className='" + className + "'")
.toString();
}
}

private static final class CacheKey {
Expand All @@ -304,8 +388,12 @@ private CacheKey(ClassLoader classLoader, String className, String propertyName)

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof CacheKey)) return false;
if (this == o) {
return true;
}
if (!(o instanceof CacheKey)) {
return false;
}
CacheKey cacheKey = (CacheKey) o;
return Objects.equals(classLoader, cacheKey.classLoader) && Objects.equals(className, cacheKey.className) && Objects.equals(propertyName, cacheKey.propertyName);
}
Expand All @@ -322,10 +410,10 @@ public int hashCode() {
@Override
public String toString() {
return "CacheKey{" +
"classLoader=" + classLoader +
", className='" + className + '\'' +
", propertyName='" + propertyName + '\'' +
'}';
"classLoader=" + classLoader +
", className='" + className + '\'' +
", propertyName='" + propertyName + '\'' +
'}';
}
}

Expand All @@ -343,7 +431,6 @@ private static Comparator<? super Method> mostMethodArgsFirst() {
return Comparator.comparingInt(Method::getParameterCount).reversed();
}

@SuppressWarnings("serial")
private static class FastNoSuchMethodException extends NoSuchMethodException {
public FastNoSuchMethodException(String methodName) {
super(methodName);
Expand Down
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package graphql.schema.bytecode;

import graphql.GraphQLException;

public class ByteCodeException extends GraphQLException {
public ByteCodeException(String message) {
super(message);
}

public ByteCodeException(String message, Throwable cause) {
super(message, cause);
}


}
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package graphql.schema.bytecode;

import graphql.Internal;

@Internal
public interface ByteCodeFetcher {
Object fetch(Object sourceObject, String propertyName);
}
Loading

Back | FazBrowse Home | New Git URL