You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
BackgroundFunctionExecutor.java failes to detect type when sub-classed #95
Exception in thread "main" java.lang.RuntimeException: Could not determine the payload type for BackgroundFunction of type com.example.MyFunctionHandler; must implement BackgroundFunction for some T at com.google.cloud.functions.invoker.BackgroundFunctionExecutor.forClass(BackgroundFunctionExecutor.java:140) at com.google.cloud.functions.invoker.BackgroundFunctionExecutor.forClass(BackgroundFunctionExecutor.java:113) at com.google.cloud.functions.invoker.runner.Invoker.startServer(Invoker.java:256) at com.google.cloud.functions.invoker.runner.Invoker.main(Invoker.java:127)
Occurs when BackgroundFunction<T> is subclassed:
public abstract class MyExtendedBackgroundFunction<T> implements BackgroundFunction<T>
and
public class MyFunctionHandler extends MyExtendedBackgroundFunction<MyReturnType>
In the class mentioned, the following function fails to detect sub-classed BackgroundFunction type:
static Optional<Type> backgroundFunctionTypeArgument(Class<? extends BackgroundFunction<?>> functionClass) {
// If this is BackgroundFunction<Foo> then the user must have implemented a method
// accept(Foo, Context), so we look for that method and return the type of its first argument.
// We must be careful because the compiler will also have added a synthetic method
// accept(Object, Context).
return Arrays.stream(functionClass.getMethods())
.filter(m -> m.getName().equals("accept") && m.getParameterCount() == 2 && m.getParameterTypes()[1] == Context.class && m.getParameterTypes()[0] != Object.class)
.map(m -> m.getGenericParameterTypes()[0])
.findFirst();
}
This fails for described MyFunctionHandler.
The intent is to add common boilerplate handling for a couple of functions which is common to all. A separate abstract function is employed, but the function fails to load because the type is not determined.
Temporary workaround is to re-implement accept() with the type in the sub-classed object with the type, but is inelegant.
I guess a better workaround is to use the RawBackgroundFunction instead of the BackgroundFunction class, and just do the deserialization yourself.. Just still seems like a bug in BackgroundFunction unless it's marked final.
Ran into this as well. The lookup only inspects the methods the concrete class has, and when accept() is inherited from a base class the only copy left is the erased accept(Object, Context) — which the filter skips on purpose to avoid the compiler's bridge method. So nothing matches and startup fails.
I have a fix that reads the type argument off the class declaration instead, walking up the hierarchy and substituting type variables as it goes. Covers the inherited accept case and the same thing two levels down; falls back to the old accept lookup when the declaration doesn't pin the type down (raw implementations). Test included.
Activity
bhoogter commentedon Jul 14, 2021
I guess a better workaround is to use the
RawBackgroundFunctioninstead of theBackgroundFunctionclass, and just do the deserialization yourself.. Just still seems like a bug inBackgroundFunctionunless it's markedfinal.rootkiller6788 commentedon Sep 25, 2026
Ran into this as well. The lookup only inspects the methods the concrete class has, and when accept() is inherited from a base class the only copy left is the erased accept(Object, Context) — which the filter skips on purpose to avoid the compiler's bridge method. So nothing matches and startup fails.
I have a fix that reads the type argument off the class declaration instead, walking up the hierarchy and substituting type variables as it goes. Covers the inherited accept case and the same thing two levels down; falls back to the old accept lookup when the declaration doesn't pin the type down (raw implementations). Test included.