As I had already few issues with combination Nomin, Eclipse and Groovy (see link1 and link2), I am again struggling with it.
My application works with JUnit Tests, both in Console via Gradle and Eclipse. But now it doesn't want to work when executing SpringBoot-Main class. Either in Eclipse nor with gradle bootRun
on Console or Eclipse Gradle Task => Same Exception
@SpringBootApplication
class CcmApplication {
static void main(String[] args) {
def ctx = SpringApplication.run CcmApplication, args
def scanner = new Scanner(System.in);
print 'Press <Return> to quit program'
scanner.nextLine()
scanner.close()
ctx.close()
}
}
Transformator Class:
class CoinMarketCapTransformer {
def resource = ClassPathResource.newInstance("coinmarketcap2coin.groovymapper")
println resource.isFile() // Prints true
println resource.isReadable() // Prints true
Nomin nomin = Nomin.newInstance(resource.filename)
CoinOnMarketPlace transform(Coin coin, @Header(name="marketName", required=true) String marketName) {
MarketPlace marketPlace = MarketPlace.newInstance(name: marketName)
CoinOnMarketPlace comp = nomin.map(coin, CoinOnMarketPlace)
comp.setMarketPlace(marketPlace)
comp
}
Coin transform(CoinOnMarketPlace comp) {
nomin.map(comp, Coin.class)
}
}
coinmarketcap2coin.groovymapper
Why I named it .groovymapper
please see in the link above. Its location is src/main/resources
import net.hemisoft.ccm.domain.CoinOnMarketPlace
import net.hemisoft.ccm.porter.Coin
mappingFor a: CoinOnMarketPlace, b: Coin
introspector exploding
automap()
a.coin.coinId = b.coinId
a.coin.name = b.name
a.coin.symbol = b.symbol
a.lastUpdate = b.lastUpdateEpoch
convert to_a: { lastUpdateEpoch -> DateUtils.convertEpochMillis(lastUpdateEpoch) }
convert to_b: { lastUpdate -> DateUtils.convertLocalDateTime(lastUpdate) }
Stacktrace:
2018-01-21 17:18:23.288 ERROR 16872 --- [sk-scheduler-10] o.s.integration.handler.LoggingHandler : org.springframework.integration.transformer.MessageTransformationException: Failed to transform Message; nested exception is org.springframework.messaging.MessageHandlingException: nested exception is org.nomin.core.NominException: org.nomin.Mapping: Recursive mapping rule a = b causes infinite loop!, failedMessage=GenericMessage [payload=net.hemisoft.ccm.porter.Coin(bitcoin, Bitcoin, BTC, 1, 11524.4, 1.0, 9.96663E9, 1.93806837423E11, 1.6817087E7, 1.6817087E7, 2.1E7, -3.42, -10.21, -15.21, 1516551267), headers={sequenceNumber=1, Server=cloudflare, sequenceSize=100, Connection=keep-alive, http_statusCode=200, Date=1516551503000, marketName=coinMarketCap, Set-Cookie=__cfduid=dba860f31637c55e679ebf89660fcc7701516551503; expires=Mon, 21-Jan-19 16:18:23 GMT; path=/; domain=.coinmarketcap.com; HttpOnly; Secure, correlationId=dd703a62-f9b4-4838-ddfe-d5f272ce894a, id=c0ce3ae4-f478-c8c4-7901-dbf0061da1e2, Content-Length=54264, contentType=application/json, timestamp=1516551503272}], failedMessage=GenericMessage [payload=net.hemisoft.ccm.porter.Coin(bitcoin, Bitcoin, BTC, 1, 11524.4, 1.0, 9.96663E9, 1.93806837423E11, 1.6817087E7, 1.6817087E7, 2.1E7, -3.42, -10.21, -15.21, 1516551267), headers={sequenceNumber=1, Server=cloudflare, sequenceSize=100, Connection=keep-alive, http_statusCode=200, Date=1516551503000, marketName=coinMarketCap, Set-Cookie=__cfduid=dba860f31637c55e679ebf89660fcc7701516551503; expires=Mon, 21-Jan-19 16:18:23 GMT; path=/; domain=.coinmarketcap.com; HttpOnly; Secure, correlationId=dd703a62-f9b4-4838-ddfe-d5f272ce894a, id=c0ce3ae4-f478-c8c4-7901-dbf0061da1e2, Content-Length=54264, contentType=application/json, timestamp=1516551503272}]
at org.springframework.integration.transformer.MessageTransformingHandler.handleRequestMessage(MessageTransformingHandler.java:95)
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:109)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:141)
at org.springframework.integration.dispatcher.BroadcastingDispatcher.invokeHandler(BroadcastingDispatcher.java:224)
at org.springframework.integration.dispatcher.BroadcastingDispatcher.dispatch(BroadcastingDispatcher.java:180)
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:73)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:438)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:388)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:181)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:160)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:47)
at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:108)
at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutput(AbstractMessageProducingHandler.java:418)
at org.springframework.integration.handler.AbstractMessageProducingHandler.produceOutput(AbstractMessageProducingHandler.java:328)
at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutputs(AbstractMessageProducingHandler.java:219)
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:115)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:141)
at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116)
at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:132)
at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:105)
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:73)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:438)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:388)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:181)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:160)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:47)
at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:108)
at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutput(AbstractMessageProducingHandler.java:418)
at org.springframework.integration.handler.AbstractMessageProducingHandler.produceOutput(AbstractMessageProducingHandler.java:328)
at org.springframework.integration.handler.MessageHandlerChain$ReplyForwardingMessageChannel.send(MessageHandlerChain.java:238)
at org.springframework.messaging.MessageChannel.send(MessageChannel.java:45)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:181)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:160)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:47)
at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:108)
at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutput(AbstractMessageProducingHandler.java:418)
at org.springframework.integration.handler.AbstractMessageProducingHandler.produceOutput(AbstractMessageProducingHandler.java:328)
at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutputs(AbstractMessageProducingHandler.java:219)
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:115)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:141)
at org.springframework.integration.handler.MessageHandlerChain.lambda$configureChain$0(MessageHandlerChain.java:124)
at org.springframework.messaging.MessageChannel.send(MessageChannel.java:45)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:181)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:160)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:47)
at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:108)
at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutput(AbstractMessageProducingHandler.java:418)
at org.springframework.integration.handler.AbstractMessageProducingHandler.produceOutput(AbstractMessageProducingHandler.java:328)
at org.springframework.integration.splitter.AbstractMessageSplitter.produceOutput(AbstractMessageSplitter.java:231)
at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutputs(AbstractMessageProducingHandler.java:219)
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:115)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:141)
at org.springframework.integration.handler.MessageHandlerChain.handleMessageInternal(MessageHandlerChain.java:110)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:141)
at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116)
at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:132)
at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:105)
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:73)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:438)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:388)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:181)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:160)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:47)
at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:108)
at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutput(AbstractMessageProducingHandler.java:418)
at org.springframework.integration.handler.AbstractMessageProducingHandler.produceOutput(AbstractMessageProducingHandler.java:328)
at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutputs(AbstractMessageProducingHandler.java:219)
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:115)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:141)
at org.springframework.integration.endpoint.PollingConsumer.handleMessage(PollingConsumer.java:129)
at org.springframework.integration.endpoint.AbstractPollingEndpoint.doPoll(AbstractPollingEndpoint.java:278)
at org.springframework.integration.endpoint.AbstractPollingEndpoint$Poller.lambda$run$0(AbstractPollingEndpoint.java:379)
at org.springframework.integration.util.ErrorHandlingTaskExecutor.lambda$execute$0(ErrorHandlingTaskExecutor.java:53)
at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50)
at org.springframework.integration.util.ErrorHandlingTaskExecutor.execute(ErrorHandlingTaskExecutor.java:51)
at org.springframework.integration.endpoint.AbstractPollingEndpoint$Poller.run(AbstractPollingEndpoint.java:373)
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:93)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: org.springframework.messaging.MessageHandlingException: nested exception is org.nomin.core.NominException: org.nomin.Mapping: Recursive mapping rule a = b causes infinite loop!, failedMessage=GenericMessage [payload=net.hemisoft.ccm.porter.Coin(bitcoin, Bitcoin, BTC, 1, 11524.4, 1.0, 9.96663E9, 1.93806837423E11, 1.6817087E7, 1.6817087E7, 2.1E7, -3.42, -10.21, -15.21, 1516551267), headers={sequenceNumber=1, Server=cloudflare, sequenceSize=100, Connection=keep-alive, http_statusCode=200, Date=1516551503000, marketName=coinMarketCap, Set-Cookie=__cfduid=dba860f31637c55e679ebf89660fcc7701516551503; expires=Mon, 21-Jan-19 16:18:23 GMT; path=/; domain=.coinmarketcap.com; HttpOnly; Secure, correlationId=dd703a62-f9b4-4838-ddfe-d5f272ce894a, id=c0ce3ae4-f478-c8c4-7901-dbf0061da1e2, Content-Length=54264, contentType=application/json, timestamp=1516551503272}]
at org.springframework.integration.handler.MethodInvokingMessageProcessor.processMessage(MethodInvokingMessageProcessor.java:107)
at org.springframework.integration.transformer.AbstractMessageProcessingTransformer.transform(AbstractMessageProcessingTransformer.java:90)
at org.springframework.integration.transformer.MessageTransformingHandler.handleRequestMessage(MessageTransformingHandler.java:89)
... 84 more
Caused by: org.nomin.core.NominException: org.nomin.Mapping: Recursive mapping rule a = b causes infinite loop!
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:83)
at org.codehaus.groovy.reflection.CachedConstructor.doConstructorInvoke(CachedConstructor.java:77)
at org.codehaus.groovy.runtime.callsite.ConstructorSite$ConstructorSiteNoUnwrap.callConstructor(ConstructorSite.java:84)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:247)
at org.nomin.core.MappingEntry.validate(MappingEntry.groovy:64)
at sun.reflect.GeneratedMethodAccessor121.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite$PogoCachedMethodSiteNoUnwrapNoCoerce.invoke(PogoMetaMethodSite.java:210)
at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite.callCurrent(PogoMetaMethodSite.java:59)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:174)
at org.nomin.core.MappingEntry.parse(MappingEntry.groovy:56)
at sun.reflect.GeneratedMethodAccessor120.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:98)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1224)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1033)
at org.codehaus.groovy.runtime.InvokerHelper.invokePogoMethod(InvokerHelper.java:1010)
at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:993)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN(ScriptBytecodeAdapter.java:168)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodNSafe(ScriptBytecodeAdapter.java:176)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodNSpreadSafe(ScriptBytecodeAdapter.java:183)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethod0SpreadSafe(ScriptBytecodeAdapter.java:198)
at org.nomin.Mapping.parse(Mapping.groovy:44)
at org.nomin.core.Nomin.findApplicable(Nomin.java:228)
at org.nomin.core.Nomin.findCachedApplicable(Nomin.java:211)
at org.nomin.core.Nomin.map(Nomin.java:201)
at org.nomin.core.Nomin.map(Nomin.java:159)
at org.nomin.core.Nomin.map(Nomin.java:156)
at org.nomin.NominMapper$map.call(Unknown Source)
at net.hemisoft.ccm.repository.CoinMarketCapTransformer.transform(CoinMarketCapTransformer.groovy:17)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:181)
at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:114)
at org.springframework.integration.util.MessagingMethodInvokerHelper$HandlerMethod.invoke(MessagingMethodInvokerHelper.java:1039)
at org.springframework.integration.util.MessagingMethodInvokerHelper.invokeHandlerMethod(MessagingMethodInvokerHelper.java:608)
at org.springframework.integration.util.MessagingMethodInvokerHelper.processInternal(MessagingMethodInvokerHelper.java:504)
at org.springframework.integration.util.MessagingMethodInvokerHelper.process(MessagingMethodInvokerHelper.java:313)
at org.springframework.integration.handler.MethodInvokingMessageProcessor.processMessage(MethodInvokingMessageProcessor.java:104)
... 86 more
UPDATE:
The following CodeSnippet from Nomin.class
works as expected when running under Test. When running via bootRun the else if on line 220 is false, what should be true.
On line 220 we have pm.sideB.isAssignableFrom(key.source)
what should result to true
, because pm.sideB
is type of Coin
, and key.source
is type of Coin
(see Screenshots).
Your problem is caused by
spring-boot-devtools
runtime dependency. When you run your application with a debugger you will find out that classesnet.hemisoft.ccm.porter.Coin
andnet.hemisoft.ccm.domain.CoinOnMarketPlace
are loaded twice using two different classloaders:sun.misc.Launcher$AppClassLoader
org.springframework.boot.devtools.restart.classloader.RestartClassLoader
This is why following part of code evaluates to
false
:Even though
key.source
andpm.sideB
seems to be the "same" classes, they are not equal because they are being managed by two different classloaders. Classes with the same canonical name are only equal inside the same classloader. And this is why your unit test works like a charm - there is no spring boot devtools involved and if you go with debugger toNomin.java:(line 220)
while running unit test, you will see thatkey.source
andpm.sideB
hold a reference to the same class inside one classloader so this expression evaluates totrue
and the mapping you defined is being used. Otherwise nomin tries to generate mapping automatically (it can be disabled withNomin.disableAutoMapping()
) and in this case, it uses default introspector -ReflectionIntrospector
. This one causes issues with Groovy classes as we already figured out in one of your previous SO questions.Solution
Disable
spring-boot-devtools
or disable restarts or at least try to exclude your domain classes from restarting application. You can check Spring Boot Devtools docs to see how to do it. I would just get rid of it - as you can see it makes your application running differently in your dev environment, so you can never be sure how it behaves when running live.