I have some code that is playing with the java stream
api:
boolean isAuthorized = authorizationByTenant.stream()
.filter(auth -> auth.getTenantName().equalsIgnoreCase(tenant))
.map(auth -> auth.getAuthorizedRoutes().get(component))
.flatMap(Collection::stream)
.anyMatch(routeDefinition -> isMatchingRoute(routePath, routeDefinition));
And I sometime get an Exception
pointing to the anyMatch
line:
java.lang.NullPointerException: null at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:273) ~[na:na] at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) ~[na:na] at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179) ~[na:na] at java.base/java.util.ArrayList$ArrayListSpliterator.tryAdvance(ArrayList.java:1602) ~[na:na] at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:129) ~[na:na] at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:527) ~[na:na] at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:513) ~[na:na] at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) ~[na:na] at java.base/java.util.stream.MatchOps$MatchOp.evaluateSequential(MatchOps.java:230) ~[na:na] at java.base/java.util.stream.MatchOps$MatchOp.evaluateSequential(MatchOps.java:196) ~[na:na] at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:na] at java.base/java.util.stream.ReferencePipeline.anyMatch(ReferencePipeline.java:632) ~[na:na] at com.mycompany.routefilter.functionality.FunctionalityService.isAuthorizedRoute(FunctionalityService.java:55) ~[classes!/:1.0-SNAPSHOT] at
My question here is what is null
? Is it routeDefinition
? And why does it make the call fails ? I expect that if routeDefinition
is null my method isMatchingRoute
would return false.
Thank you.
CodePudding user response:
The NullPointerException is occurring in your .flatMap(Collection::stream)
expression.
I created a similar example and expanded the method reference into a lambda. When causing a null value there you will see the stacktrace become more clear.
Example:
Stream.of("foo")
.map(foo -> (List<String>) null)
.flatMap(Collection::stream)
.anyMatch(string -> "gnarly".equals(string)); // LINE 13
Leads to
Exception in thread "main" java.lang.NullPointerException
at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:271)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
at java.base/java.util.stream.Streams$StreamBuilderImpl.tryAdvance(Streams.java:397)
at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:127)
at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:502)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:488)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
at java.base/java.util.stream.MatchOps$MatchOp.evaluateSequential(MatchOps.java:230)
at java.base/java.util.stream.MatchOps$MatchOp.evaluateSequential(MatchOps.java:196)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.anyMatch(ReferencePipeline.java:528)
at Test.main(Test.java:13)
When I replace it with:
Stream.of("foo")
.map(foo -> (List<String>) null)
.flatMap(strings -> strings.stream()) // LINE 11
.anyMatch(string -> "gnarly".equals(string)); // LINE 12
Then the exception becomes:
Exception in thread "main" java.lang.NullPointerException
at Test.lambda$main$1(Test.java:11)
at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:271)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
at java.base/java.util.stream.Streams$StreamBuilderImpl.tryAdvance(Streams.java:397)
at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:127)
at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:502)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:488)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
at java.base/java.util.stream.MatchOps$MatchOp.evaluateSequential(MatchOps.java:230)
at java.base/java.util.stream.MatchOps$MatchOp.evaluateSequential(MatchOps.java:196)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.anyMatch(ReferencePipeline.java:528)
at Test.main(Test.java:12)
Here you see at the top of the stack trace that the real cause is in line 11. A bit confusing indeed, but I guess the JRE cannot report exceptions occurring within method references with line number accuracy.
CodePudding user response:
Your code is mainly failing at .flatMap(Collection::stream)
.
This is mostly because you are not handling null check in .map(auth -> auth.getAuthorizedRoutes().get(component))
. So to avoid this scenario, you can either do filter before calling flatmap:
boolean isAuthorized = authorizationByTenant.stream()
.filter(auth -> auth.getTenantName().equalsIgnoreCase(tenant))
.map(auth -> auth.getAuthorizedRoutes().get(component))
.filter(auth -> auth.getAuthorizedRoutes().get(component)!= null)
.flatMap(Collection::stream)
.anyMatch(routeDefinition -> isMatchingRoute(routePath, routeDefinition));
The hint which I have found out is based on java.base/java.util.stream.MatchOps$MatchOp.evaluateSequential(MatchOps.java:230) ~[na:na] at
line of stacktrace.
MatchOps$MatchOp
is accepting predicate and determines if elements of source stream (in your case stream generated from flatMap
) matches predicate or not. So this confirms that your flatMap is generating null
value which in turn takes input of your map
call.