javacitrus-framework

Citrus Simulator throws Nullpointer Exception when receiving serialized xml data


I'm running Citrus Simulator as a spring-boot application to simulate web services responding to HTTP POST calls. Everything works fine in "lab" conditions. As long as I send data by hand via Postman, curl, or a browser plugin I have no problems and clean answers. Once the data is sent by another application however, I get a nasty nullpointer exception.

It took me a while to pinpoint the issue but now I know it to be the format of the sent data.

Sending pretty printed xml data results in a perfect answer. As soon as I serialize the xml though, it won't be processed by Citrus Simulator correctly and I'll get a nullpointer exception.

Using curl I'm also able to provoke the error simply by killing the format of the sent xml file.

Edit It's not the format directly. Both applications, when sending data in a pretty printed format, will generate a HTTP Header Metadata with some (strange) contents -> curl sends Metadata="\t\t\t", postman sends Metadata="\n ". Sending a serialized xml, both applications send an empty Metadata Header thus resulting in a nullpointer exception right here (MessageService.java:60) -> StringUtils.abbreviate(headerEntry.getValue().toString(), 255))));

java.lang.NullPointerException: null
at com.consol.citrus.simulator.service.MessageService.lambda$saveMessage$0(MessageService.java:60) ~[citrus-simulator-starter-1.0.3.jar:na]
at java.util.Iterator.forEachRemaining(Iterator.java:116) ~[na:1.8.0_201]
at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801) ~[na:1.8.0_201]
at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580) ~[na:1.8.0_201]
at com.consol.citrus.simulator.service.MessageService.saveMessage(MessageService.java:58) ~[citrus-simulator-starter-1.0.3.jar:na]
at com.consol.citrus.simulator.service.MessageService$$FastClassBySpringCGLIB$$be0e8eaa.invoke(<generated>) ~[citrus-simulator-starter-1.0.3.jar:na]
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) ~[spring-core-4.3.21.RELEASE.jar:4.3.21.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:736) ~[spring-aop-4.3.21.RELEASE.jar:4.3.21.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.3.21.RELEASE.jar:4.3.21.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) ~[spring-tx-4.3.21.RELEASE.jar:4.3.21.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282) ~[spring-tx-4.3.21.RELEASE.jar:4.3.21.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) ~[spring-tx-4.3.21.RELEASE.jar:4.3.21.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.21.RELEASE.jar:4.3.21.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:671) ~[spring-aop-4.3.21.RELEASE.jar:4.3.21.RELEASE]
at com.consol.citrus.simulator.service.MessageService$$EnhancerBySpringCGLIB$$f26ac16e.saveMessage(<generated>) ~[citrus-simulator-starter-1.0.3.jar:na]
at com.consol.citrus.simulator.service.ActivityService.saveScenarioMessage(ActivityService.java:124) ~[citrus-simulator-starter-1.0.3.jar:na]
at com.consol.citrus.simulator.service.ActivityService$$FastClassBySpringCGLIB$$1784762a.invoke(<generated>) ~[citrus-simulator-starter-1.0.3.jar:na]
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) ~[spring-core-4.3.21.RELEASE.jar:4.3.21.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:736) ~[spring-aop-4.3.21.RELEASE.jar:4.3.21.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.3.21.RELEASE.jar:4.3.21.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) ~[spring-tx-4.3.21.RELEASE.jar:4.3.21.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282) ~[spring-tx-4.3.21.RELEASE.jar:4.3.21.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) ~[spring-tx-4.3.21.RELEASE.jar:4.3.21.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.21.RELEASE.jar:4.3.21.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:671) ~[spring-aop-4.3.21.RELEASE.jar:4.3.21.RELEASE]
at com.consol.citrus.simulator.service.ActivityService$$EnhancerBySpringCGLIB$$35f927ee.saveScenarioMessage(<generated>) ~[citrus-simulator-starter-1.0.3.jar:na]
at com.consol.citrus.simulator.endpoint.EndpointMessageHandler.saveScenarioMessage(EndpointMessageHandler.java:58) ~[citrus-simulator-starter-1.0.3.jar:na]
at com.consol.citrus.simulator.endpoint.EndpointMessageHandler.handleReceivedMessage(EndpointMessageHandler.java:51) ~[citrus-simulator-starter-1.0.3.jar:na]
at com.consol.citrus.simulator.scenario.ScenarioEndpoint.messageReceived(ScenarioEndpoint.java:111) ~[citrus-simulator-starter-1.0.3.jar:na]
at com.consol.citrus.simulator.scenario.ScenarioEndpoint.receive(ScenarioEndpoint.java:88) ~[citrus-simulator-starter-1.0.3.jar:na]
at com.consol.citrus.simulator.scenario.SVManagerScenario.receive(SVManagerScenario.java:60) ~[classes/:na]
at com.consol.citrus.simulator.scenario.SVManagerScenario.run(SVManagerScenario.java:24) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_201]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_201]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_201]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_201]
at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:215) ~[spring-core-4.3.21.RELEASE.jar:4.3.21.RELEASE]
at com.consol.citrus.simulator.service.ScenarioExecutionService.lambda$null$3(ScenarioExecutionService.java:150) ~[citrus-simulator-starter-1.0.3.jar:na]
at org.springframework.util.ReflectionUtils.doWithLocalMethods(ReflectionUtils.java:491) ~[spring-core-4.3.21.RELEASE.jar:4.3.21.RELEASE]
at com.consol.citrus.simulator.service.ScenarioExecutionService.lambda$startScenarioAsync$4(ScenarioExecutionService.java:111) ~[citrus-simulator-starter-1.0.3.jar:na]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) ~[na:1.8.0_201]
at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[na:1.8.0_201]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[na:1.8.0_201]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[na:1.8.0_201]
at java.lang.Thread.run(Thread.java:748) ~[na:1.8.0_201]

Unfortunately I'm not able to get the latest builds of citrus. My setup is running on java 1.8 and citrus 2.7.1

Has anyone ever had a similar issue with an older version of Citrus Simulator?

I'm grateful for any help


Solution

  • I've never investigated in the cause of this (well, it's some null-header obviously), but a simple workaround (not solution!) is to override the com.consol.citrus.simulator.serviceMessageService bean as following:

    @Service
    @Primary
    @Transactional
    public class NullHeaderAwareMessageService extends MessageService {
    
        private final MessageRepository messageRepository;
    
        @Autowired
        public NullHeaderAwareMessageService(MessageRepository messageRepository) {
            super(messageRepository);
    
            this.messageRepository = messageRepository;
        }
    
        @Override
        public Message saveMessage(Message.Direction direction, String payload, String citrusMessageId, Map<String, Object> headers) {
            Message message = new Message();
            message.setDate(new Date());
            message.setDirection(direction);
            message.setPayload(payload);
            message.setCitrusMessageId(citrusMessageId);
            if (headers != null) {
                headers.entrySet().stream()
                        .filter(headerEntry -> headerEntry.getValue() != null)
                        .forEach(headerEntry -> message.addHeader(
                                    new MessageHeader(headerEntry.getKey(),
                                            StringUtils.abbreviate(headerEntry.getValue().toString(), 255))));
            }
    
            return messageRepository.save(message);
        }
    }