'java.lang.ClassCastException: class org.eclipse.jetty.server.Request$1 in Spring Cloud Function

Sample repo available on Github.

Given this simple Spring Cloud Function application:

@SpringBootApplication
class SpringServerlessGcpApplication{

    private val random = SecureRandom()

    @Bean
    fun uppercase(): (String) -> String = { it.uppercase(Locale.getDefault()) }

    @Bean
    fun random(): (String) -> String = { "$it${random.nextDouble()}" }

}

fun main(args: Array<String>) {
    runApplication<SpringServerlessGcpApplication>(*args)
}
spring.cloud.function.definition=uppercase|random

I can invoke it passing a String:

$>curl -X POST http://localhost:8080 -d 'Hello'
$>"HELLO0.8119601710812162"%                                                                            

However if the String contains a space character it throws an exception, why?

$>curl -X POST http://localhost:8080 -d 'Hello world'
Mar 30, 2022 9:17:45 AM com.google.cloud.functions.invoker.HttpFunctionExecutor service
SEVERE: Failed to execute org.springframework.cloud.function.adapter.gcp.GcfJarLauncher
java.lang.ClassCastException: class org.eclipse.jetty.server.Request$1 cannot be cast to class [B (org.eclipse.jetty.server.Request$1 is in unnamed module of loader org.codehaus.plexus.classworlds.realm.ClassRealm @3fa87583; [B is in module java.base of loader 'bootstrap')
        at org.springframework.messaging.converter.StringMessageConverter.convertFromInternal(StringMessageConverter.java:60)
        at org.springframework.messaging.converter.AbstractMessageConverter.fromMessage(AbstractMessageConverter.java:185)
        at org.springframework.messaging.converter.AbstractMessageConverter.fromMessage(AbstractMessageConverter.java:176)
        at org.springframework.cloud.function.context.config.SmartCompositeMessageConverter.fromMessage(SmartCompositeMessageConverter.java:48)
        at org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry$FunctionInvocationWrapper.convertInputMessageIfNecessary(SimpleFunctionRegistry.java:1282)
        at org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry$FunctionInvocationWrapper.convertInputIfNecessary(SimpleFunctionRegistry.java:1057)
        at org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry$FunctionInvocationWrapper.doApply(SimpleFunctionRegistry.java:696)
        at org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry$FunctionInvocationWrapper.lambda$andThen$0(SimpleFunctionRegistry.java:623)
        at org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry$FunctionInvocationWrapper.doApply(SimpleFunctionRegistry.java:699)
        at org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry$FunctionInvocationWrapper.apply(SimpleFunctionRegistry.java:551)
        at org.springframework.cloud.function.adapter.gcp.FunctionInvoker.service(FunctionInvoker.java:98)
        at org.springframework.cloud.function.adapter.gcp.GcfJarLauncher.service(GcfJarLauncher.java:53)
        at com.google.cloud.functions.invoker.HttpFunctionExecutor.service(HttpFunctionExecutor.java:69)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
        at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:755)
        at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:547)
        at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:233)
        at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1297)
        at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:188)
        at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:485)
        at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:186)
        at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1212)
        at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
        at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)
        at com.google.cloud.functions.invoker.runner.Invoker$NotFoundHandler.handle(Invoker.java:390)
        at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)
        at org.eclipse.jetty.server.Server.handle(Server.java:500)
        at org.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:383)
        at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:547)
        at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:375)
        at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:270)
        at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311)
        at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:103)
        at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:117)
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:336)
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:313)
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:171)
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.produce(EatWhatYouKill.java:135)
        at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:806)
        at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:938)
        at java.base/java.lang.Thread.run(Thread.java:830)



Solution 1:[1]

First, use curl correctly.

if you are sending a POST, you'll only need the one of the --data* options, and you won't need the -X POST portion.

If you have anything that needs encoding, use the --data-urlencode version of the command line option.

$ curl --data-urlencode "Hello World" http://localhost:8080

which is the equivalent of

$ curl --data "Hello%20World" http://localhost:8080

But this doesn't really fix the root issue, as your uppercase and random functions are causing this issue.

They should produce sane values, and in your project, it's expecting a result of Content-Type that is application/json.

The signature of your function should match ...

Function<Message<BufferedReader>, Message<byte[]>> function;

Your functions are instead returning a raw internal Jetty class on Request that implements the request specific Jetty HttpInput class (that is what org.eclipse.jetty.server.Request$1 is).

As a result the MessageConverter list goes through ...

  1. JsonMessageConverter - skips, as it's not a valid message in format application/json
  2. ByteArrayMessageConverter - skips, as it's not a byte[]
  3. StringMessageConverter - fails when it attempts to convert this raw Request$1 to a byte[]

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1