Releases: graphql-java/graphql-java
19.4
This is a security bugfix release containing PR #3133. This adds a limit to the depth of grammar rules, to prevent stack overflow. See the full details on the original PR: #3112.
This release also includes backported fixes to ensure MANIFEST.MF
is the first entry in the JAR file and removes sun.misc
from Import-Package
header. See the full details on the original PRs: #3091 and #3097.
What's Changed
- Backported the fix to remove sun.misc by @schaefa in #3099
- Backported #3112 into 19.x branch by @bbakerman in #3133
Full Changelog: v19.3...v19.4
20.0
We are pleased to announce the release of graphql-java 20.0. Special thanks to each of the 200+ contributors over the years, who have made this milestone possible.
Breaking changes
Aligning parseValue
coercion with JS reference implementation
We have made changes to String, Boolean, Float, and Int parseValue
coercion, to be consistent with the reference JS implementation. The key change is parseValue
is now stricter on accepted inputs.
- String
parseValue
now requires input is of typeString
. For example, a Number input123
or a Boolean inputtrue
will no longer be accepted. - Boolean
parseValue
now requires input is of typeBoolean
. For example, a String input"true"
will no longer be accepted. - Float
parseValue
now requires input is of typeNumber
. For example, a String input"3.14"
will no longer be accepted. - Int
parseValue
now requires input is of typeNumber
. For example, a String input"42"
will no longer be accepted.
String parseValue
changes: #3030
Boolean, Float, and Int parseValue
changes: #3042
JS reference implementation: https://github.com/graphql/graphql-js/blob/main/src/type/scalars.ts
Notable Changes
Record Like Property Fetching Support
We have now added the ability to find properties via "Record like" naming. We call it "Record like" based on Java 14 record
classes but in fact any class with a method named directly as the graphql field is named will work.
If you had this graphql object type declared
type Person {
name : String
address : String
}
then this Java record
would be supported for fetching values via the method names name()
and address()
public record Person (String name, String address)
and equally a non record class like this would also work
public class Person {
public String name() { return "Harry Potter"; }
public String address() { return "4 Privet Drive, Little Whinging"; }
}
We still have Java Bean (aka POJO) getter naming support like public String getName()
however now the "record like" name()
method will be used in preference and then the getName()
methods will be used if that's not present.
This means there is a new behavior if you had weird POJOs likes this
public class WeirdPerson {
public String name() { return "Harry Potter"; }
public String getName() { return "Tom Riddle"; }
}
A property fetch for name
will now return Harry Potter
and not Tom Riddle
as it previously would have.
This is a behavioral breaking change but on balance we think this behavior is the most correct going forward.
Improved Data Fetching
The PropertyDataFetcher
class is the most common data fetcher used in graphql-java. It uses Java reflection to get field values from objects based on field name.
This was logically the following
Method method = findMethod(fieldname);
method.invoke(object);
with the method lookup cached for performance reasons.
However there is mechanism in the JVM that provides even faster object reflective access.
See
https://wttech.blog/blog/2020/method-handles-and-lambda-metafactory/
https://www.optaplanner.org/blog/2018/01/09/JavaReflectionButMuchFaster.html
java.lang.invoke.LambdaMetafactory#metafactory
is an arcane mechanism that can be used to create virtual method lambdas that give fast access to call object methods. It turns out to be significantly faster that Java reflection and only marginally slower that directly invoking a method.
If you use PropertyDataFetcher
a lot (and chances are you do) then this should give improved performance.
The raw benchmarks are as follows
Java 8
Benchmark Mode Cnt Score Error Units
GetterAccessBenchmark.measureDirectAccess thrpt 15 81199548.105 ± 2717206.756 ops/s 0% slower (baseline)
GetterAccessBenchmark.measureLambdaAccess thrpt 15 79622345.446 ± 1183553.379 ops/s 2% slower
GetterAccessBenchmark.measureReflectionAccess thrpt 15 46102664.133 ± 4091595.318 ops/s 50% slower
Java 17
Benchmark Mode Cnt Score Error Units
GetterAccessBenchmark.measureDirectAccess thrpt 15 458411420.717 ± 34329506.990 ops/s 0%
GetterAccessBenchmark.measureLambdaAccess thrpt 15 334158880.091 ± 10666070.698 ops/s 27% slower
GetterAccessBenchmark.measureReflectionAccess thrpt 15 63181868.566 ± 3887367.970 ops/s 86% slower
It's worth noting that while the headline numbers here look impressive, the property fetching represents a smaller portion of what happens during graphql engine execution.
It probably won't be enough to keep Elon Musk happy but all performance improvements help and at scale they help the most.
Lightweight Data Fetchers
A DataFetcher
gets invoked with a calling environment context object called graphql.schema.DataFetchingEnvironment
. This is quite a rich object that contains all sorts of useful information.
However simple (aka trivial) data fetchers like PropertyDataFetcher
they don't need access to such a rich object. They just need the source object, the field name and the field type
To marginally help performance, we have introduced a graphql.schema.LightDataFetcher
for this use case
public interface LightDataFetcher<T> extends TrivialDataFetcher<T> {
T get(GraphQLFieldDefinition fieldDefinition, Object sourceObject, Supplier<DataFetchingEnvironment> environmentSupplier) throws Exception;
}
PropertyDataFetcher
implements this and hence this lowers the object allocation at scale (which reduces memory pressure) and will make the system marginally faster to fetch data.
Performance Improvements by avoid object allocations
We are always trying to wring out the most performance we can in graphql-java and so we reviewed our object allocations and found places where we can make savings.
These won't make dramatic performance savings but at scale all these things add up, reducing memory pressure and improving throughput marginally.
Locale is now available in Coercing and Parsing
The graphql.schema.Coercing
interface used by scalars can now receive a Locale
object that indicates the calling Locale
. The same is true for the parsing code via graphql.parser.ParserEnvironment#getLocale
A custom scalar implementation could use the locale to decide how to coerce values.
Easier ways to build common objects
We have added extra builders on the GraphQLError
, ErrorClassification
and ExecutionResult
interfaces that make it easier to build instances of these common classes.
The deprecated NextGen engine has been removed
The NextGen engine was an experimental feature that explored what it might take to build a new graphql engine. In many ways it was a success as it taught us a bunch of about graph algorithms and what works and what does not.
While it had some value, on balance it was not going to become production ready and so we deprecated it a while back and it has finally been removed.
What's Changed
- docs: update latest release badge to 19 by @setchy in #2918
- Fix printing directives when they contain something like a formatting… by @jmartisk in #2920
- Implement pretty printer by @felipe-gdr in #2894
- Fix snapshot badge by @dondonz in #2924
- Remove @fetch and nextgen engine by @dondonz in #2923
- Fix up field visibility doco example by @dondonz in #2927
- We can rename scalar types by @bbakerman in #2928
- Add deprecation date to all deprecated methods and fields by @dondonz in #2929
- Fix field visibility bug with enum with enum args by @felipe-gdr in #2926
- Adding Locale to Coercing and hence ValueResolver by @bbakerman in #2912
- Removes the deprecated execute methods from GraphQL by @bbakerman in #2932
- Removing deprecated methods from tests - part 1 by @dondonz in #2930
- Reproduction of renaming scalars and applied directives bug by @bbakerman in #2934
- Remove redundant NaN check, already handled in GraphqlFloatCoercing by @dondonz in #2936
- Diff counts are the same by @bbakerman in #2935
- Change Instrumentation production implementations to use non deprecated methods by @bbakerman in #2931
- Make parseValue nullable and update Coercing javadoc by @dondonz in https://github.com/graphql-java/gra...
19.3
The 19.3 bug fix release has been created
What's Changed
- 19.x fix - use class loader on i18n by @bbakerman in #3038
Full Changelog: v19.2...v19.3
19.2
The 19.2 bug fix release has been created
What's Changed
- Stable fix for #2934 by @bbakerman in #2943
- Stable port of Fix field visibility bug with enum with enum args (#2926) by @bbakerman in #2944
- Stable port of Fix printing directives when they contain something li… by @bbakerman in #2945
- Stable port of Diff counts are the same by @bbakerman in #2946
- Stable port of #2940 by @bbakerman in #2947
Full Changelog: v19.1...v19.2
19.1
This bug fix release was made to address a specific NullPointerException
problem if consumers are explicitly setting the ExecutionInput
to null
See #2908 for the code details.
The other fixes are included because they are... well... fixes and where ready at the time.
What's Changed
- Defaults Locale when calling validation by @bbakerman in #2908
- Handles isDeprecated not being present in the json by @bbakerman in #2910
- Fix typo in description of skip directive by @acanda in #2915
- Xuorig Fix PR - Edge case with GraphQLTypeReference and Schema Transforms by @bbakerman in #2906
- Reduce calculation for fragments in ExecutableNormalizedOperation by @dondonz in #2911
New Contributors
Full Changelog: v19.0...v19.1
19.0
This is release 19.0 of GraphQL Java. It contains one breaking change.
It contains one security related bugfix hardening GraphQL Java more against malicious requests: #2892
GraphQL Java now shades Antlr runtime to prevent any further dependency conflicts. Antlr is used internally for parsing and validating of GraphQL requests and SDL. #2854
It includes some performance improvements (#2786, #2769, #2839) and several bugfixes and general improvements.
Breaking change
#2769 is an improvement to reduce object allocation. It can contain a breaking change if you would implement your own ChainedInstrumentation.
Change in behaviour
#2878 introduces i18n for validation error messages, and by default will set locale to the JVM default locale
#2799 changes the behaviour of the AST printer to use the shortest form available for query operation if possible. While semantically this is not a change, it might affect you.
Bugfixes
#2892 Security bugfix to prevent DOS attacks
#2818 Fix silent thread leak for chained instrumentation
#2825 Fixup Introspection input field deprecation filterting
#2842 fix runtime exception for deep async queries
#2856 SchemaPrinter description bugfix
Improvements
#2786 performance improvements for validation
#2854 Shade Antlr Runtime
#2896 Update DataLoader to 3.2.0
#2878 i18n for validation error messages
#2881 Improve SchemaPrinter
#2872 Improve AST compact printing
#2846 Subscription root field valiation
All changes
all PRs: https://github.com/graphql-java/graphql-java/milestone/38?closed=1
18.3
17.4
18.2
This bug fix release fixes the double variable coercion problem identified in #2819, and introduces RawVariables
and CoercedVariables
to indicate whether variables have been coerced.
Note: This is a bug fix release. Only changes to fix #2819 have been cherry picked in this release. Other merged changes will be released separately as v19.
What's Changed
- Cherry pick raw and coerced variable refactor for 18.x branch by @dondonz in #2861
- Cherry pick double variable coercion fix by @dondonz in #2867
- Bugfix cherry pick: RawVariables and CoercedVariables are public API (#2868) by @dondonz in #2870
Full Changelog: v18.1...v18.2
18.1
This bug fix release contains an important fix
The latest 18.0 version of graphql-java changed the way raw values are resolved to canonical values.
However this revealed a bug in MaxQueryXXX instrumentation where invalid values (null being present for non nullable input values) caused an exception rather than generating a graphql error. This is not a behavior we intended.
The bug is only present if you use graphql.analysis.MaxQueryDepthInstrumentation
and graphql.analysis.MaxQueryDepthInstrumentation
What's Changed
- Make public static fields immutable in ScalarInfo by @kilink in #2770
- Improve NonNullableValueCoercedAsNullException message by @dondonz in #2774
- Make public static fields immutable in DirectiveInfo by @kilink in #2781
- Replace usage of Stack with Deque / ArrayDeque by @kilink in #2780
- Fix argument / format string mismatches by @kilink in #2779
- Fixing the missing description for enum values for introspection to schema conversion by @ashpak-shaikh in #2778
- Fix
TypeRuntimeWiring.enumValues()
error message by @martinbonnin in #2782 - GraphQL Specification link updated by @firatkucuk in #2785
- Variable document compilation now handles enums properly AND also null input values by @bbakerman in #2784
- Add ParserOption to ignore single-line comments by @jord1e in #2788
- Add missing annotations in DelegatingDataFetchingEnvironment by @kilink in #2797
- Fix instances of incorrectly sized Maps and Sets by @kilink in #2798
- Use jetbrains annotations by @bbakerman in #2801
- Make some inner classes static by @kilink in #2804
- Fix
AstPrinter
to print field descriptions by @david-castaneda in #2808 - Make some static fields final by @kilink in #2803
- Max query depth called later in beginExecuteOperation by @bbakerman in #2773
New Contributors
- @kilink made their first contribution in #2770
- @ashpak-shaikh made their first contribution in #2778
- @martinbonnin made their first contribution in #2782
- @firatkucuk made their first contribution in #2785
- @david-castaneda made their first contribution in #2808
Full Changelog: v18.0...v18.1