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...