/* * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com * The software in this package is published under the terms of the CPAL v1.0 * license, a copy of which has been included with this distribution in the * LICENSE.txt file. */ package org.mule.runtime.core.processor.chain; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; import static java.util.Optional.empty; import static java.util.stream.Collectors.toList; import static org.apache.commons.lang.RandomStringUtils.randomNumeric; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.mule.runtime.api.message.Message.of; import static org.mule.runtime.core.api.construct.Flow.builder; import static org.mule.runtime.core.api.lifecycle.LifecycleUtils.stopIfNeeded; import static org.mule.runtime.core.api.processor.MessageProcessors.newChain; import static org.mule.runtime.core.api.processor.ReactiveProcessor.ProcessingType.CPU_INTENSIVE; import static org.mule.tck.junit4.AbstractReactiveProcessorTestCase.Mode.BLOCKING; import static org.mule.tck.junit4.AbstractReactiveProcessorTestCase.Mode.NON_BLOCKING; import static org.mule.tck.util.MuleContextUtils.mockContextWithServices; import static reactor.core.publisher.Flux.from; import org.mule.runtime.api.exception.MuleException; import org.mule.runtime.api.lifecycle.InitialisationException; import org.mule.runtime.api.lifecycle.Lifecycle; import org.mule.runtime.api.message.ErrorType; import org.mule.runtime.api.message.Message; import org.mule.runtime.core.DefaultEventContext; import org.mule.runtime.core.api.Event; import org.mule.runtime.core.api.EventContext; import org.mule.runtime.core.api.MuleContext; import org.mule.runtime.core.api.MuleSession; import org.mule.runtime.core.api.config.MuleConfiguration; import org.mule.runtime.core.api.construct.Flow; import org.mule.runtime.core.api.construct.FlowConstruct; import org.mule.runtime.core.api.construct.FlowConstructAware; import org.mule.runtime.core.api.context.MuleContextAware; import org.mule.runtime.core.api.execution.ExceptionContextProvider; import org.mule.runtime.core.api.processor.MessageProcessorBuilder; import org.mule.runtime.core.api.processor.MessageProcessorChain; import org.mule.runtime.core.api.processor.Processor; import org.mule.runtime.core.api.processor.strategy.ProcessingStrategyFactory; import org.mule.runtime.core.api.scheduler.SchedulerService; import org.mule.runtime.core.context.notification.DefaultFlowCallStack; import org.mule.runtime.core.exception.ErrorTypeLocator; import org.mule.runtime.core.processor.AbstractInterceptingMessageProcessor; import org.mule.runtime.core.processor.ResponseMessageProcessorAdapter; import org.mule.runtime.core.processor.strategy.BlockingProcessingStrategyFactory; import org.mule.runtime.core.processor.strategy.DefaultFlowProcessingStrategyFactory; import org.mule.runtime.core.processor.strategy.DirectProcessingStrategyFactory; import org.mule.runtime.core.processor.strategy.ProactorProcessingStrategyFactory; import org.mule.runtime.core.processor.strategy.ReactorProcessingStrategyFactory; import org.mule.runtime.core.processor.strategy.WorkQueueProcessingStrategyFactory; import org.mule.runtime.core.routing.ChoiceRouter; import org.mule.runtime.core.routing.ScatterGatherRouter; import org.mule.runtime.core.routing.filters.AcceptAllFilter; import org.mule.runtime.core.util.ObjectUtils; import org.mule.tck.junit4.AbstractReactiveProcessorTestCase; import org.mule.tck.size.SmallTest; import java.util.Collection; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.reactivestreams.Publisher; @RunWith(Parameterized.class) @SmallTest @SuppressWarnings("deprecation") public class DefaultMessageProcessorChainTestCase extends AbstractReactiveProcessorTestCase { protected MuleContext muleContext; protected boolean synchronous; private AtomicInteger nonBlockingProcessorsExecuted = new AtomicInteger(0); private ProcessingStrategyFactory processingStrategyFactory; @Rule public ExpectedException expectedException = ExpectedException.none(); @Parameterized.Parameters public static Collection<Object[]> parameters() { return asList(new Object[][] { {new DefaultFlowProcessingStrategyFactory(), BLOCKING}, {new ReactorProcessingStrategyFactory(), BLOCKING}, {new ProactorProcessingStrategyFactory(), BLOCKING}, {new WorkQueueProcessingStrategyFactory(), BLOCKING}, {new BlockingProcessingStrategyFactory(), BLOCKING}, {new DirectProcessingStrategyFactory(), BLOCKING}, {new DefaultFlowProcessingStrategyFactory(), NON_BLOCKING}, {new ReactorProcessingStrategyFactory(), NON_BLOCKING}, {new ProactorProcessingStrategyFactory(), NON_BLOCKING}, {new WorkQueueProcessingStrategyFactory(), NON_BLOCKING}, {new BlockingProcessingStrategyFactory(), NON_BLOCKING}, {new DirectProcessingStrategyFactory(), NON_BLOCKING}}); } private Flow flow; public DefaultMessageProcessorChainTestCase(ProcessingStrategyFactory processingStrategyFactory, Mode mode) { super(mode); this.processingStrategyFactory = processingStrategyFactory; } @Before public void before() throws MuleException { nonBlockingProcessorsExecuted.set(0); muleContext = mockContextWithServices(); ErrorTypeLocator errorTypeLocator = mock(ErrorTypeLocator.class); ErrorType errorType = mock(ErrorType.class); ExceptionContextProvider exceptionContextProvider = mock(ExceptionContextProvider.class); MuleConfiguration muleConfiguration = mock(MuleConfiguration.class); when(muleConfiguration.isContainerMode()).thenReturn(false); when(muleConfiguration.getId()).thenReturn(randomNumeric(3)); when(muleConfiguration.getShutdownTimeout()).thenReturn(1000L); when(muleContext.getConfiguration()).thenReturn(muleConfiguration); when(muleContext.getErrorTypeLocator()).thenReturn(errorTypeLocator); when(muleContext.getExceptionContextProviders()).thenReturn(singletonList(exceptionContextProvider)); when(errorTypeLocator.lookupErrorType((Exception) any())).thenReturn(errorType); flow = builder("flow", muleContext).processingStrategyFactory(processingStrategyFactory).build(); flow.initialise(); flow.start(); } @After public void after() throws MuleException { stopIfNeeded(muleContext.getSchedulerService()); flow.stop(); flow.dispose(); } @Test public void testMPChain() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain(getAppendingMP("1"), getAppendingMP("2"), getAppendingMP("3")); assertEquals("0123", process(builder.build(), getTestEventUsingFlow("0")).getMessage().getPayload().getValue()); } /* * Any MP returns null: - Processing doesn't proceed - Result of chain is Null */ @Test public void testMPChainWithNullReturn() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); AppendingMP mp1 = getAppendingMP("1"); AppendingMP mp2 = getAppendingMP("2"); ReturnNullMP nullmp = new ReturnNullMP(); AppendingMP mp3 = getAppendingMP("3"); builder.chain(mp1, mp2, nullmp, mp3); Event requestEvent = getTestEventUsingFlow("0"); assertNull(process(builder.build(), requestEvent)); // mp1 assertSame(requestEvent.getMessage(), mp1.event.getMessage()); assertNotSame(mp1.event, mp1.resultEvent); assertEquals("01", mp1.resultEvent.getMessage().getPayload().getValue()); // mp2 assertSame(mp1.resultEvent.getMessage(), mp2.event.getMessage()); assertNotSame(mp2.event, mp2.resultEvent); assertEquals("012", mp2.resultEvent.getMessage().getPayload().getValue()); // nullmp assertSame(mp2.resultEvent.getMessage(), nullmp.event.getMessage()); assertEquals("012", nullmp.event.getMessage().getPayload().getValue()); // mp3 assertNull(mp3.event); } /* * Any MP returns null: - Processing doesn't proceed - Result of chain is Null */ @Test public void testMPChainWithVoidReturn() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); AppendingMP mp1 = getAppendingMP("1"); AppendingMP mp2 = getAppendingMP("2"); ReturnVoidMP voidmp = new ReturnVoidMP(); AppendingMP mp3 = getAppendingMP("3"); builder.chain(mp1, mp2, voidmp, mp3); Event requestEvent = getTestEventUsingFlow("0"); assertEquals("0123", process(builder.build(), requestEvent).getMessage().getPayload().getValue()); // mp1 // assertSame(requestEvent, mp1.event); assertNotSame(mp1.event, mp1.resultEvent); // mp2 // assertSame(mp1.resultEvent, mp2.event); assertNotSame(mp2.event, mp2.resultEvent); // void mp assertEquals(mp2.resultEvent.getMessage(), voidmp.event.getMessage()); // mp3 assertThat(mp3.event.getMessage().getPayload().getValue(), equalTo(mp2.resultEvent.getMessage().getPayload().getValue())); assertEquals(mp3.event.getMessage().getPayload().getValue(), "012"); } @Test public void testMPChainWithNullReturnAtEnd() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain(getAppendingMP("1"), getAppendingMP("2"), getAppendingMP("3"), new ReturnNullMP()); assertNull(process(builder.build(), getTestEventUsingFlow("0"))); } @Test public void testMPChainWithVoidReturnAtEnd() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain(getAppendingMP("1"), getAppendingMP("2"), getAppendingMP("3"), new ReturnVoidMP()); assertEquals("0123", process(builder.build(), getTestEventUsingFlow("0")).getMessage().getPayload().getValue()); } @Test public void testMPChainWithBuilder() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain(getAppendingMP("1")); builder.chain((MessageProcessorBuilder) () -> getAppendingMP("2")); builder.chain(getAppendingMP("3")); assertEquals("0123", process(builder.build(), getTestEventUsingFlow("0")).getMessage().getPayload().getValue()); } @Test public void testInterceptingMPChain() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain(new AppendingInterceptingMP("1"), new AppendingInterceptingMP("2"), new AppendingInterceptingMP("3")); assertEquals("0before1before2before3after3after2after1", process(builder.build(), getTestEventUsingFlow("0")).getMessage().getPayload().getValue()); } @Test public void testInterceptingMPChainWithNullReturn() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); AppendingInterceptingMP lastMP = new AppendingInterceptingMP("3"); builder.chain(new AppendingInterceptingMP("1"), new AppendingInterceptingMP("2"), new ReturnNullInterceptongMP(), lastMP); assertNull(process(builder.build(), getTestEventUsingFlow("0"))); assertFalse(lastMP.invoked); } @Test public void testInterceptingMPChainWithVoidReturn() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); AppendingInterceptingMP lastMP = new AppendingInterceptingMP("3"); builder.chain(new AppendingInterceptingMP("1"), new AppendingInterceptingMP("2"), new ReturnNullInterceptongMP(), lastMP); assertNull(process(builder.build(), getTestEventUsingFlow("0"))); assertFalse(lastMP.invoked); } @Test public void testMixedMPChain() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain(new AppendingInterceptingMP("1"), getAppendingMP("2"), getAppendingMP("3"), new AppendingInterceptingMP("4"), getAppendingMP("5")); assertEquals("0before123before45after4after1", process(builder.build(), getTestEventUsingFlow("0")).getMessage().getPayload().getValue()); } @Test // Whenever there is a IMP that returns null the final result is null public void testMixedMPChainWithNullReturn1() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain(new AppendingInterceptingMP("1"), new ReturnNullInterceptongMP(), getAppendingMP("2"), getAppendingMP("3"), new AppendingInterceptingMP("4"), getAppendingMP("5")); assertNull(process(builder.build(), getTestEventUsingFlow("0"))); } @Test // Whenever there is a IMP that returns null the final result is null public void testMixedMPChainWithVoidReturn1() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain(new AppendingInterceptingMP("1"), new ReturnVoidMPInterceptongMP(), getAppendingMP("2"), getAppendingMP("3"), new AppendingInterceptingMP("4"), getAppendingMP("5")); assertThat(process(builder.build(), getTestEventUsingFlow("0")).getMessage().getPayload().getValue(), equalTo("0before1after1")); } @Test // Whenever there is a IMP that returns null the final result is null public void testMixedMPChainWithNullReturn2() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain(new AppendingInterceptingMP("1"), getAppendingMP("2"), new ReturnNullInterceptongMP(), getAppendingMP("3"), new AppendingInterceptingMP("4"), getAppendingMP("5")); assertNull(process(builder.build(), getTestEventUsingFlow("0"))); } @Test // Whenever there is a IMP that returns null the final result is null public void testMixedMPChainWithVoidlReturn2() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain(new AppendingInterceptingMP("1"), getAppendingMP("2"), new ReturnVoidMPInterceptongMP(), getAppendingMP("3"), new AppendingInterceptingMP("4"), getAppendingMP("5")); assertEquals("0before12after1", process(builder.build(), getTestEventUsingFlow("0")).getMessage().getPayload().getValue()); } @Test // A simple MP that returns null does not affect flow as long as it's not at the // end public void testMixedMPChainWithNullReturn3() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain(new AppendingInterceptingMP("1"), new ReturnNullMP(), getAppendingMP("2"), getAppendingMP("3"), new AppendingInterceptingMP("4"), getAppendingMP("5")); assertNull(process(builder.build(), getTestEventUsingFlow("0"))); } @Test // A simple MP that returns null does not affect flow as long as it's not at the // end public void testMixedMPChainWithVoidReturn3() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain(new AppendingInterceptingMP("1"), new ReturnVoidMP(), getAppendingMP("2"), getAppendingMP("3"), new AppendingInterceptingMP("4"), getAppendingMP("5")); assertEquals("0before123before45after4after1", process(builder.build(), getTestEventUsingFlow("0")).getMessage().getPayload().getValue()); } @Test // A simple MP that returns null does not affect flow as long as it's not at the // end public void testMixedMPChainWithNullReturn4() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain(new AppendingInterceptingMP("1"), getAppendingMP("2"), new ReturnNullMP(), getAppendingMP("3"), new AppendingInterceptingMP("4"), getAppendingMP("5")); assertNull(process(builder.build(), getTestEventUsingFlow("0"))); } @Test // A simple MP that returns null does not affect flow as long as it's not at the // end public void testMixedMPChainWithVoidReturn4() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain(new AppendingInterceptingMP("1"), getAppendingMP("2"), new ReturnVoidMP(), getAppendingMP("3"), new AppendingInterceptingMP("4"), getAppendingMP("5")); assertEquals("0before123before45after4after1", process(builder.build(), getTestEventUsingFlow("0")).getMessage().getPayload().getValue()); } @Test // A simple MP that returns null does not affect flow as long as it's not at the // end public void testMixedMPChainWithNullReturn5() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain(new AppendingInterceptingMP("1"), getAppendingMP("2"), getAppendingMP("3"), new ReturnNullMP(), new AppendingInterceptingMP("4"), getAppendingMP("5")); assertNull(process(builder.build(), getTestEventUsingFlow("0"))); } @Test // A simple MP that returns null does not affect flow as long as it's not at the // end public void testMixedMPChainWithVoidReturn5() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain(new AppendingInterceptingMP("1"), getAppendingMP("2"), getAppendingMP("3"), new ReturnVoidMP(), new AppendingInterceptingMP("4"), getAppendingMP("5")); assertEquals("0before123before45after4after1", process(builder.build(), getTestEventUsingFlow("0")).getMessage().getPayload().getValue()); } @Test // A simple MP at the end of a single level chain causes chain to return null public void testMixedMPChainWithNullReturnAtEnd() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain(new AppendingInterceptingMP("1"), getAppendingMP("2"), getAppendingMP("3"), new AppendingInterceptingMP("4"), getAppendingMP("5"), new ReturnNullMP()); assertNull(process(builder.build(), getTestEventUsingFlow("0"))); } @Test // A simple MP at the end of a single level chain causes chain to return null public void testMixedMPChainWithVoidReturnAtEnd() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain(new AppendingInterceptingMP("1"), getAppendingMP("2"), getAppendingMP("3"), new AppendingInterceptingMP("4"), getAppendingMP("5"), new ReturnVoidMP()); assertEquals("0before123before45after4after1", process(builder.build(), getTestEventUsingFlow("0")).getMessage().getPayload().getValue()); } @Test public void testNestedMPChain() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain(getAppendingMP("1"), new DefaultMessageProcessorChainBuilder().chain(getAppendingMP("a"), getAppendingMP("b")).build(), getAppendingMP("2")); assertEquals("01ab2", process(builder.build(), getTestEventUsingFlow("0")).getMessage().getPayload().getValue()); } @Test public void testNestedMPChainWithNullReturn() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain( getAppendingMP("1"), new DefaultMessageProcessorChainBuilder() .chain(getAppendingMP("a"), new ReturnNullMP(), getAppendingMP("b")).build(), new ReturnNullMP(), getAppendingMP("2")); assertNull(process(builder.build(), getTestEventUsingFlow("0"))); } @Test public void testNestedMPChainWithVoidReturn() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain( getAppendingMP("1"), new DefaultMessageProcessorChainBuilder() .chain(getAppendingMP("a"), new ReturnVoidMP(), getAppendingMP("b")).build(), new ReturnVoidMP(), getAppendingMP("2")); assertEquals("01ab2", process(builder.build(), getTestEventUsingFlow("0")).getMessage().getPayload().getValue()); } @Test public void testNestedMPChainWithNullReturnAtEndOfNestedChain() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain(getAppendingMP("1"), new DefaultMessageProcessorChainBuilder() .chain(getAppendingMP("a"), getAppendingMP("b"), new ReturnNullMP()).build(), getAppendingMP("2")); assertNull(process(builder.build(), getTestEventUsingFlow("0"))); } @Test public void testNestedMPChainWithVoidReturnAtEndOfNestedChain() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain(getAppendingMP("1"), new DefaultMessageProcessorChainBuilder() .chain(getAppendingMP("a"), getAppendingMP("b"), new ReturnVoidMP()).build(), getAppendingMP("2")); assertEquals("01ab2", process(builder.build(), getTestEventUsingFlow("0")).getMessage().getPayload().getValue()); } @Test public void testNestedMPChainWithNullReturnAtEndOfNestedChainWithNonInterceptingWrapper() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); final MessageProcessorChain nested = new DefaultMessageProcessorChainBuilder().chain(getAppendingMP("a"), getAppendingMP("b"), new ReturnNullMP()) .build(); nested.setMuleContext(muleContext); builder.chain(getAppendingMP("1"), event -> nested.process(event), getAppendingMP("2")); assertNull("012", process(builder.build(), getTestEventUsingFlow("0"))); } @Test public void testNestedMPChainWithVoidReturnAtEndOfNestedChainWithNonInterceptingWrapper() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); final MessageProcessorChain nested = new DefaultMessageProcessorChainBuilder().chain(getAppendingMP("a"), getAppendingMP("b"), new ReturnVoidMP()) .build(); nested.setMuleContext(muleContext); builder.chain(getAppendingMP("1"), event -> nested.process(Event.builder(event) .message(event.getMessage()).flow(flow).build()), getAppendingMP("2")); assertEquals("01ab2", process(builder.build(), getTestEventUsingFlow("0")).getMessage().getPayload().getValue()); } @Test public void testNestedInterceptingMPChain() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain(new AppendingInterceptingMP("1"), new DefaultMessageProcessorChainBuilder() .chain(new AppendingInterceptingMP("a"), new AppendingInterceptingMP("b")).build(), new AppendingInterceptingMP("2")); assertEquals("0before1beforeabeforebafterbafterabefore2after2after1", process(builder.build(), getTestEventUsingFlow("0")).getMessage().getPayload().getValue()); } @Test public void testNestedInterceptingMPChainWithNullReturn() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain(new AppendingInterceptingMP("1"), new DefaultMessageProcessorChainBuilder() .chain(new AppendingInterceptingMP("a"), new ReturnNullInterceptongMP(), new AppendingInterceptingMP("b")) .build(), new AppendingInterceptingMP("2")); assertNull(process(builder.build(), getTestEventUsingFlow("0"))); } @Test public void testNestedInterceptingMPChainWithVoidReturn() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain(new AppendingInterceptingMP("1"), new DefaultMessageProcessorChainBuilder() .chain(new AppendingInterceptingMP("a"), new ReturnVoidMPInterceptongMP(), new AppendingInterceptingMP("b")) .build(), new AppendingInterceptingMP("2")); assertThat(process(builder.build(), getTestEventUsingFlow("0")).getMessage().getPayload().getValue(), equalTo("0before1beforeaafterabefore2after2after1")); } @Test public void testNestedMixedMPChain() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain(getAppendingMP("1"), new DefaultMessageProcessorChainBuilder() .chain(new AppendingInterceptingMP("a"), getAppendingMP("b")).build(), new AppendingInterceptingMP("2")); assertEquals("01beforeabafterabefore2after2", process(builder.build(), getTestEventUsingFlow("0")).getMessage().getPayload().getValue()); } @Test public void testInterceptingMPChainStopFlow() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain(new AppendingInterceptingMP("1"), new AppendingInterceptingMP("2", true), new AppendingInterceptingMP("3")); assertEquals("0before1after1", process(builder.build(), getTestEventUsingFlow("0")).getMessage().getPayload().getValue()); } /** * Note: Stopping the flow of a nested chain causes the nested chain to return early, but does not stop the flow of the parent * chain. */ @Test public void testNestedInterceptingMPChainStopFlow() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain(new AppendingInterceptingMP("1"), new DefaultMessageProcessorChainBuilder() .chain(new AppendingInterceptingMP("a", true), new AppendingInterceptingMP("b")).build(), new AppendingInterceptingMP("3")); assertEquals("0before1before3after3after1", process(builder.build(), getTestEventUsingFlow("0")).getMessage().getPayload().getValue()); } @Test public void testMPChainLifecycle() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); AppendingInterceptingMP mp1 = new AppendingInterceptingMP("1"); AppendingInterceptingMP mp2 = new AppendingInterceptingMP("2"); Processor chain = builder.chain(mp1, mp2).build(); ((MuleContextAware) chain).setMuleContext(muleContext); ((FlowConstructAware) chain).setFlowConstruct(mock(FlowConstruct.class)); ((Lifecycle) chain).initialise(); ((Lifecycle) chain).start(); ((Lifecycle) chain).stop(); ((Lifecycle) chain).dispose(); assertLifecycle(mp1); assertLifecycle(mp2); } @Test public void testNestedMPChainLifecycle() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); DefaultMessageProcessorChainBuilder nestedBuilder = new DefaultMessageProcessorChainBuilder(); AppendingInterceptingMP mp1 = new AppendingInterceptingMP("1"); AppendingInterceptingMP mp2 = new AppendingInterceptingMP("2"); AppendingInterceptingMP mpa = new AppendingInterceptingMP("a"); AppendingInterceptingMP mpb = new AppendingInterceptingMP("b"); Processor chain = builder.chain(mp1, nestedBuilder.chain(mpa, mpb).build(), mp2).build(); ((MuleContextAware) chain).setMuleContext(muleContext); ((FlowConstructAware) chain).setFlowConstruct(mock(FlowConstruct.class)); ((Lifecycle) chain).initialise(); ((Lifecycle) chain).start(); ((Lifecycle) chain).stop(); ((Lifecycle) chain).dispose(); assertLifecycle(mp1); assertLifecycle(mp2); assertLifecycle(mpa); assertLifecycle(mpb); } @Test public void testNoneIntercepting() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain(new TestNonIntercepting(), new TestNonIntercepting(), new TestNonIntercepting()); Event restul = process(builder.build(), getTestEventUsingFlow("")); assertEquals("MessageProcessorMessageProcessorMessageProcessor", restul.getMessage().getPayload().getValue()); } @Test public void testAllIntercepting() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain(new TestIntercepting(), new TestIntercepting(), new TestIntercepting()); Event restul = process(builder.build(), getTestEventUsingFlow("")); assertEquals("InterceptingMessageProcessorInterceptingMessageProcessorInterceptingMessageProcessor", restul.getMessage().getPayload().getValue()); } @Test public void testMix() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain(new TestIntercepting(), new TestNonIntercepting(), new TestNonIntercepting(), new TestIntercepting(), new TestNonIntercepting(), new TestNonIntercepting()); Event restul = process(builder.build(), getTestEventUsingFlow("")); assertEquals("InterceptingMessageProcessorMessageProcessorMessageProcessorInterceptingMessageProcessorMessageProcessorMessageProcessor", restul.getMessage().getPayload().getValue()); } @Test public void testMixStaticFactoryt() throws Exception { MessageProcessorChain chain = newChain(new TestIntercepting(), new TestNonIntercepting(), new TestNonIntercepting(), new TestIntercepting(), new TestNonIntercepting(), new TestNonIntercepting()); Event restul = process(chain, getTestEventUsingFlow("")); assertEquals("InterceptingMessageProcessorMessageProcessorMessageProcessorInterceptingMessageProcessorMessageProcessorMessageProcessor", restul.getMessage().getPayload().getValue()); } @Test public void testMix2() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain(new TestNonIntercepting(), new TestIntercepting(), new TestNonIntercepting(), new TestNonIntercepting(), new TestNonIntercepting(), new TestIntercepting()); Event restul = process(builder.build(), getTestEventUsingFlow("")); assertEquals("MessageProcessorInterceptingMessageProcessorMessageProcessorMessageProcessorMessageProcessorInterceptingMessageProcessor", restul.getMessage().getPayload().getValue()); } @Test public void testMix2StaticFactory() throws Exception { MessageProcessorChain chain = newChain(new TestNonIntercepting(), new TestIntercepting(), new TestNonIntercepting(), new TestNonIntercepting(), new TestNonIntercepting(), new TestIntercepting()); Event result = process(chain, getTestEventUsingFlow("")); assertEquals("MessageProcessorInterceptingMessageProcessorMessageProcessorMessageProcessorMessageProcessorInterceptingMessageProcessor", result.getMessage().getPayload().getValue()); } @Test public void testResponseProcessor() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); final ResponseMessageProcessorAdapter responseMessageProcessorAdapter = new ResponseMessageProcessorAdapter(getAppendingMP("3")); responseMessageProcessorAdapter.setMuleContext(muleContext); builder.chain(getAppendingMP("1"), responseMessageProcessorAdapter, getAppendingMP("2")); assertThat(process(builder.build(), getTestEventUsingFlow("0")).getMessage().getPayload().getValue(), equalTo("0123")); } @Test public void testResponseProcessorInNestedChain() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); final ResponseMessageProcessorAdapter responseMessageProcessorAdapter = new ResponseMessageProcessorAdapter(getAppendingMP("c")); responseMessageProcessorAdapter.setMuleContext(muleContext); builder.chain( getAppendingMP("1"), newChain(getAppendingMP("a"), responseMessageProcessorAdapter, getAppendingMP("b")), getAppendingMP("2")); assertThat(process(builder.build(), getTestEventUsingFlow("0")).getMessage().getPayload().getValue(), equalTo("01abc2")); } @Test public void testNestedResponseProcessor() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); final ResponseMessageProcessorAdapter innerResponseMessageProcessorAdapter = new ResponseMessageProcessorAdapter(getAppendingMP("4")); final ResponseMessageProcessorAdapter responseMessageProcessorAdapter = new ResponseMessageProcessorAdapter(newChain(innerResponseMessageProcessorAdapter, getAppendingMP("3"))); builder.chain(getAppendingMP("1"), responseMessageProcessorAdapter, getAppendingMP("2")); process(builder.build(), getTestEventUsingFlow("0")); assertThat(process(builder.build(), getTestEventUsingFlow("0")).getMessage().getPayload().getValue(), equalTo("01234")); } @Test public void testNestedResponseProcessorEndOfChain() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); final MessageProcessorChain chain = newChain(singletonList(getAppendingMP("1"))); final ResponseMessageProcessorAdapter responseMessageProcessorAdapter = new ResponseMessageProcessorAdapter(chain); responseMessageProcessorAdapter.setMuleContext(muleContext); builder.chain(responseMessageProcessorAdapter); process(builder.build(), getTestEventUsingFlow("0")); assertThat(process(builder.build(), getTestEventUsingFlow("0")).getMessage().getPayload().getValue(), equalTo("01")); } @Test public void testAll() throws Exception { ScatterGatherRouter scatterGatherRouter = new ScatterGatherRouter(); scatterGatherRouter.addRoute(getAppendingMP("1")); scatterGatherRouter.addRoute(getAppendingMP("2")); scatterGatherRouter.addRoute(getAppendingMP("3")); scatterGatherRouter.setMuleContext(muleContext); scatterGatherRouter.initialise(); scatterGatherRouter.start(); Event event = getTestEventUsingFlow("0"); final MessageProcessorChain chain = newChain(singletonList(scatterGatherRouter)); Message result = process(chain, Event.builder(event).message(event.getMessage()).build()).getMessage(); assertThat(result.getPayload().getValue(), instanceOf(List.class)); List<Message> resultMessage = (List<Message>) result.getPayload().getValue(); assertThat(resultMessage.stream().map(msg -> msg.getPayload().getValue()).collect(toList()).toArray(), is(equalTo(new String[] {"01", "02", "03"}))); scatterGatherRouter.stop(); scatterGatherRouter.dispose(); } @Test public void testChoice() throws Exception { ChoiceRouter choiceRouter = new ChoiceRouter(); choiceRouter.addRoute(newChain(getAppendingMP("1")), new AcceptAllFilter()); choiceRouter.addRoute(newChain(getAppendingMP("2")), new AcceptAllFilter()); choiceRouter.addRoute(newChain(getAppendingMP("3")), new AcceptAllFilter()); assertThat(process(newChain(choiceRouter), getTestEventUsingFlow("0")).getMessage().getPayload().getValue(), equalTo("01")); } @Test public void testExceptionAfter() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain(getAppendingMP("1"), new ExceptionThrowingMessageProcessor()); expectedException.expect(IllegalStateException.class); process(builder.build(), getTestEventUsingFlow("0")); } @Test public void testExceptionBefore() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain(new ExceptionThrowingMessageProcessor(), getAppendingMP("1")); expectedException.expect(IllegalStateException.class); process(builder.build(), getTestEventUsingFlow("0")); } @Test public void testExceptionBetween() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain(getAppendingMP("1"), new ExceptionThrowingMessageProcessor(), getAppendingMP("2")); expectedException.expect(IllegalStateException.class); process(builder.build(), getTestEventUsingFlow("0")); } @Test public void testExceptionInResponse() throws Exception { DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(); builder.chain(new ResponseMessageProcessorAdapter(new ExceptionThrowingMessageProcessor()), getAppendingMP("1")); expectedException.expect(IllegalStateException.class); process(builder.build(), getTestEventUsingFlow("0")); } @Override protected Event process(Processor messageProcessor, Event event) throws Exception { if (messageProcessor instanceof MuleContextAware) { ((MuleContextAware) messageProcessor).setMuleContext(muleContext); } if (messageProcessor instanceof FlowConstructAware) { ((FlowConstructAware) messageProcessor).setFlowConstruct(flow); } try { return super.process(messageProcessor, event); } finally { final SchedulerService schedulerService = muleContext.getSchedulerService(); } } private AppendingMP getAppendingMP(String append) { return new NonBlockingAppendingMP(append); } static class TestNonIntercepting implements Processor { @Override public Event process(Event event) throws MuleException { return Event.builder(event).message(of(event.getMessage().getPayload().getValue() + "MessageProcessor")).build(); } } static class TestIntercepting extends AbstractInterceptingMessageProcessor { @Override public Event process(Event event) throws MuleException { return processNext(Event.builder(event) .message(of(event.getMessage().getPayload().getValue() + "InterceptingMessageProcessor")).build()); } } private void assertLifecycle(AppendingInterceptingMP mp) { assertTrue(mp.flowConstuctInjected); assertTrue(mp.muleContextInjected); assertTrue(mp.initialised); assertTrue(mp.started); assertTrue(mp.stopped); assertTrue(mp.disposed); } class NonBlockingAppendingMP extends AppendingMP { /** * Force the proactor to change the thread. */ @Override public ProcessingType getProcessingType() { return CPU_INTENSIVE; } public NonBlockingAppendingMP(String append) { super(append); } @Override public Event process(Event event) throws MuleException { nonBlockingProcessorsExecuted.incrementAndGet(); return super.process(event); } } class AppendingMP implements Processor, Lifecycle, FlowConstructAware, MuleContextAware { String appendString; boolean muleContextInjected; boolean flowConstuctInjected; boolean initialised; boolean started; boolean stopped; boolean disposed; Event event; Event resultEvent; public AppendingMP(String append) { this.appendString = append; } @Override public Event process(final Event event) throws MuleException { return innerProcess(event); } private Event innerProcess(Event event) { this.event = event; Event result = Event.builder(event).message(of(event.getMessage().getPayload().getValue() + appendString)).build(); this.resultEvent = result; return result; } @Override public void initialise() throws InitialisationException { initialised = true; } @Override public void start() throws MuleException { started = true; } @Override public void stop() throws MuleException { stopped = true; } @Override public void dispose() { disposed = true; } @Override public String toString() { return ObjectUtils.toString(this); } @Override public void setMuleContext(MuleContext context) { this.muleContextInjected = true; } @Override public void setFlowConstruct(FlowConstruct flowConstruct) { this.flowConstuctInjected = true; } } class AppendingInterceptingMP extends AbstractInterceptingMessageProcessor implements FlowConstructAware, Lifecycle { String appendString; boolean muleContextInjected; boolean flowConstuctInjected; boolean initialised; boolean started; boolean stopped; boolean disposed; private boolean stopProcessing; boolean invoked; public AppendingInterceptingMP(String appendString) { this(appendString, false); } public AppendingInterceptingMP(String appendString, boolean stopProcessing) { this.appendString = appendString; this.stopProcessing = stopProcessing; } @Override public Event process(Event event) throws MuleException { if (stopProcessing) { return event; } Event result = processNext(appendBefore(event)); if (result != null) { return appendAfter(result); } else { return result; } } @Override public Publisher<Event> apply(Publisher<Event> publisher) { if (stopProcessing) { return publisher; } else { return from(publisher).map(before -> appendBefore(before)).transform(applyNext()) .map(after -> { if (after != null) { return appendAfter(after); } else { return after; } }); } } private Event appendAfter(Event after) { return Event.builder(after).message(of(after.getMessage().getPayload().getValue() + "after" + appendString)).build(); } private Event appendBefore(Event before) { return Event.builder(before).message(of(before.getMessage().getPayload().getValue() + "before" + appendString)).build(); } @Override public void initialise() throws InitialisationException { initialised = true; } @Override public void start() throws MuleException { started = true; } @Override public void stop() throws MuleException { stopped = true; } @Override public void dispose() { disposed = true; } @Override public String toString() { return ObjectUtils.toString(this); } @Override public void setMuleContext(MuleContext context) { this.muleContextInjected = true; super.setMuleContext(context); } @Override public void setFlowConstruct(FlowConstruct flowConstruct) { this.flowConstuctInjected = true; super.setFlowConstruct(flowConstruct); } } static class ReturnNullMP implements Processor { Event event; @Override public Event process(Event event) throws MuleException { this.event = event; return null; } } static class ReturnNullInterceptongMP extends AbstractInterceptingMessageProcessor { @Override public Event process(Event event) throws MuleException { return null; } } private static class ReturnVoidMP implements Processor { Event event; @Override public Event process(Event event) throws MuleException { this.event = event; return event; } } static class ReturnVoidMPInterceptongMP extends AbstractInterceptingMessageProcessor { @Override public Event process(Event event) throws MuleException { return event; } } protected Event getTestEventUsingFlow(Object data) { Event event = mock(Event.class); EventContext eventContext = DefaultEventContext.create(flow, TEST_CONNECTOR_LOCATION); Message message = of(data); when(event.getFlowCallStack()).thenReturn(new DefaultFlowCallStack()); when(event.getMessage()).thenReturn(message); when(event.getSession()).thenReturn(mock(MuleSession.class)); when(event.getError()).thenReturn(empty()); when(event.getContext()).thenReturn(eventContext); return event; } public static class ExceptionThrowingMessageProcessor implements Processor { @Override public Event process(Event event) throws MuleException { throw new IllegalStateException(); } } }