Though there is exactly 1 parameter for Service i am always hitting argument mismatch when using spring with kotlin combination.
i have also debugged
org.springframework.remoting.support.RemoteInvocation.invoke
method
i could see it is passing exactly 1 argument and correct targetObject. But invoke(targetObject, this.arguments);
resulted in parameter mismatch.
Source Code:
https://github.com/c-nnooka/RabbitMqRemotingKotlin
Exception
Caused by: **java.lang.Throwable: argument type mismatch**
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_161]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_161]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_161]
at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_161]
at org.springframework.remoting.support.RemoteInvocation.invoke(RemoteInvocation.java:215) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.remoting.support.DefaultRemoteInvocationExecutor.invoke(DefaultRemoteInvocationExecutor.java:39) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.remoting.support.RemoteInvocationBasedExporter.invoke(RemoteInvocationBasedExporter.java:78) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.remoting.support.RemoteInvocationBasedExporter.invokeAndCreateResult(RemoteInvocationBasedExporter.java:114) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.amqp.remoting.service.AmqpInvokerServiceExporter.onMessage(AmqpInvokerServiceExporter.java:80) ~[spring-amqp-2.0.2.RELEASE.jar:2.0.2.RELEASE]
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:1457) ~[spring-rabbit-2.0.2.RELEASE.jar:2.0.2.RELEASE]
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.actualInvokeListener(AbstractMessageListenerContainer.java:1348) ~[spring-rabbit-2.0.2.RELEASE.jar:2.0.2.RELEASE]
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:1324) ~[spring-rabbit-2.0.2.RELEASE.jar:2.0.2.RELEASE]
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:1303) ~[spring-rabbit-2.0.2.RELEASE.jar:2.0.2.RELEASE]
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:785) ~[spring-rabbit-2.0.2.RELEASE.jar:2.0.2.RELEASE]
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:769) ~[spring-rabbit-2.0.2.RELEASE.jar:2.0.2.RELEASE]
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$700(SimpleMessageListenerContainer.java:77) ~[spring-rabbit-2.0.2.RELEASE.jar:2.0.2.RELEASE]
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1010) ~[spring-rabbit-2.0.2.RELEASE.jar:2.0.2.RELEASE]
at java.lang.Thread.run(Thread.java:748) ~[?:1.8.0_161]
at org.springframework.remoting.support.RemoteInvocationUtils.fillInClientStackTraceIfPossible(RemoteInvocationUtils.java:45) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.remoting.support.RemoteInvocationResult.recreate(RemoteInvocationResult.java:156) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.amqp.remoting.client.AmqpClientInterceptor.invoke(AmqpClientInterceptor.java:78) ~[spring-amqp-2.0.2.RELEASE.jar:2.0.2.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) ~[spring-aop-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at com.sun.proxy.$Proxy108.findJobById(Unknown Source) ~[?:?]
Interface Details:
interface IJobQueryService{
fun findJobById(args : QueryJobArgs) : QueryJobResponse
}
interface IJobService : IJobQueryService
Service Implementation Details:
class JobController(val jobQueryService: IJobQueryService) : IJobService{
override fun findJobById(args: QueryJobArgs): QueryJobResponse {
return jobQueryService.findJobById(args)
}
}
BeanConfiguration: (Note: Not specifying complete configuration for simplicity)
@Bean
fun rmsExporter(rabbitTemplate: RabbitTemplate):AmqpInvokerServiceExporter {
val exporter = AmqpInvokerServiceExporter();
exporter.amqpTemplate = rabbitTemplate;
exporter.service = JobController(JobQueryProvider());
exporter.serviceInterface = IJobService::class.java
exporter.messageConverter = producerJackson2MessageConverter()
return exporter;
}
@Bean
fun rmxProxy(rabbitTemplate: RabbitTemplate) : AmqpProxyFactoryBean {
val proxy = AmqpProxyFactoryBean();
proxy.amqpTemplate = rabbitTemplate;
proxy.serviceInterface = IJobService::class.java
proxy.routingKey = "rms.webservice.api"
return proxy;
}
Invocation Details
val jobservice = applicationContext.getBean(IJobService::class.java)
println("Remoting Is On => " + jobservice.findJobById(QueryJobArgs().apply { job = Job().apply { id = 1 } } ))
The problem that you try to transport JSON over the network.
Therefore an AmqpClientInterceptor
wraps your QueryJobArgs
into the RemoteInvocation
object and already that one is serialized to the JSON like:
(Body:'{"methodName":"findJobById","parameterTypes":["com.example.rabbiitmqremoting.args.job.QueryJobArgs"],"arguments":[{"job":{"id":1,"createdBy":null}}],"attributes":null}' MessageProperties [headers={__TypeId__=org.springframework.remoting.support.RemoteInvocation}, contentType=application/json, contentEncoding=UTF-8, contentLength=167, deliveryMode=PERSISTENT, priority=0, deliveryTag=0])
Pay attention to the __TypeId__=org.springframework.remoting.support.RemoteInvocation
header. This one is used on the consumer side to deserialize a content to the RemoteInvocation
. But since there is no an (automatic) info for the arguments
to be deseriailized to the QueryJobArgs
they remains as a LinkedHashMap
. Therefore Caused by: java.lang.Throwable: argument type mismatch
.
As a workaround I suggest you to come back to the SimpleMessageConverter
which will use a standard Java serialization mechanism.
UPDATE
OK! You know I've hacked this with the JSON as well.
So, I did this:
@Autowired
lateinit var objectMapper: ObjectMapper
@PostConstruct
fun init() {
this.objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY)
}
And injected this objectMapper
into all the Jackson
converters presented in your RabbitConfig
. Now it works as expected I assume:
Hellow From RMS
Remoting Is On => com.example.rabbiitmqremoting.response.QueryJobResponse@151bf776
Also I removed all the modifications related to the messageHandlerMethodFactory
. Doesn't look like it is involved in the RPC.
However you may need it for some other stuff. Different story though...