/* * 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.construct.processor; import static org.hamcrest.CoreMatchers.instanceOf; import static org.junit.rules.ExpectedException.none; import static org.mockito.Matchers.any; import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mule.runtime.api.message.Message.of; import static org.mule.runtime.core.api.lifecycle.LifecycleUtils.stopIfNeeded; import static org.mule.runtime.core.context.notification.PipelineMessageNotification.PROCESS_COMPLETE; import static org.mule.runtime.core.context.notification.PipelineMessageNotification.PROCESS_END; import static org.mule.runtime.core.context.notification.PipelineMessageNotification.PROCESS_START; import static org.mule.tck.util.MuleContextUtils.mockContextWithServices; import org.mule.runtime.api.component.ComponentIdentifier; import org.mule.runtime.api.exception.MuleException; import org.mule.runtime.api.message.ErrorType; 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.TransformationService; import org.mule.runtime.core.api.context.notification.EnrichedServerNotification; import org.mule.runtime.core.api.processor.MessageProcessorChainBuilder; import org.mule.runtime.core.api.processor.Processor; import org.mule.runtime.core.config.DefaultMuleConfiguration; import org.mule.runtime.core.context.notification.AsyncMessageNotification; import org.mule.runtime.core.context.notification.ErrorHandlerNotification; import org.mule.runtime.core.context.notification.PipelineMessageNotification; import org.mule.runtime.core.context.notification.ServerNotificationManager; import org.mule.runtime.core.exception.ErrorHandlerFactory; import org.mule.runtime.core.exception.ErrorTypeLocator; import org.mule.runtime.core.exception.MessagingException; import org.mule.runtime.core.internal.construct.DefaultFlowBuilder.DefaultFlow; import org.mule.runtime.core.management.stats.AllStatistics; import org.mule.tck.junit4.AbstractReactiveProcessorTestCase; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; 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.mockito.ArgumentMatcher; @RunWith(Parameterized.class) public class PipelineMessageNotificationTestCase extends AbstractReactiveProcessorTestCase { private Event event; private ServerNotificationManager notificationManager; private TestPipeline pipeline; private final String pipelineName = "testPipeline"; private EventContext context; @Rule public ExpectedException thrown = none(); public PipelineMessageNotificationTestCase(Mode mode) { super(mode); } @Before public void createMocks() throws Exception { muleContext.dispose(); muleContext = mockContextWithServices(); when(muleContext.getStatistics()).thenReturn(new AllStatistics()); when(muleContext.getConfiguration()).thenReturn(new DefaultMuleConfiguration()); when(muleContext.getDefaultErrorHandler()).thenReturn(new ErrorHandlerFactory().createDefault(muleContext)); notificationManager = mock(ServerNotificationManager.class); when(muleContext.getNotificationManager()).thenReturn(notificationManager); ErrorTypeLocator errorTypeLocator = mock(ErrorTypeLocator.class); ErrorType errorType = mock(ErrorType.class); when(errorTypeLocator.lookupErrorType(any(Throwable.class))).thenReturn(errorType); when(errorTypeLocator.<String, Throwable>lookupComponentErrorType(any(ComponentIdentifier.class), any(Throwable.class))) .thenReturn(errorType); when(muleContext.getErrorTypeLocator()).thenReturn(errorTypeLocator); pipeline = new TestPipeline(pipelineName, muleContext); when(muleContext.getTransformationService()).thenReturn(new TransformationService(muleContext)); context = DefaultEventContext.create(pipeline, TEST_CONNECTOR_LOCATION); } @After public void after() throws MuleException { stopIfNeeded(pipeline); stopIfNeeded(muleContext.getSchedulerService()); } @Test public void send() throws Exception { pipeline.initialise(); pipeline.start(); event = Event.builder(context).message(of("request")).flow(pipeline).build(); process(pipeline, event); verifySucess(); } @Test public void requestResponseException() throws Exception { pipeline.setExceptionListener(new ErrorHandlerFactory().createDefault(muleContext)); List<Processor> processors = new ArrayList<>(); processors.add(new ExceptionThrowingMessageProcessor()); pipeline.setMessageProcessors(processors); pipeline.initialise(); pipeline.start(); event = Event.builder(context).message(of("request")).flow(pipeline).build(); thrown.expect(instanceOf(MessagingException.class)); thrown.expectCause(instanceOf(IllegalStateException.class)); try { process(pipeline, event); } finally { verifyException(); } } private void verifySucess() { verify(notificationManager, times(1)) .fireNotification(argThat(new PipelineMessageNotificiationArgumentMatcher(PROCESS_START, false, event))); verify(notificationManager, times(1)) .fireNotification(argThat(new PipelineMessageNotificiationArgumentMatcher(PROCESS_END, false, event))); verify(notificationManager, times(1)) .fireNotification(argThat(new PipelineMessageNotificiationArgumentMatcher(PROCESS_COMPLETE, false, event))); verify(notificationManager, times(3)).fireNotification(any(PipelineMessageNotification.class)); } private void verifyException() { verify(notificationManager, times(1)) .fireNotification(argThat(new PipelineMessageNotificiationArgumentMatcher(PROCESS_START, false, event))); verify(notificationManager, times(1)) .fireNotification(argThat(new PipelineMessageNotificiationArgumentMatcher(PROCESS_COMPLETE, true, null))); verify(notificationManager, times(1)) .fireNotification(argThat(new PipelineMessageNotificiationArgumentMatcher(ErrorHandlerNotification.PROCESS_START, false, null))); verify(notificationManager, times(1)) .fireNotification(argThat(new PipelineMessageNotificiationArgumentMatcher(ErrorHandlerNotification.PROCESS_END, false, null))); verify(notificationManager, times(4)).fireNotification(any(PipelineMessageNotification.class)); } private class TestPipeline extends DefaultFlow { CountDownLatch latch = new CountDownLatch(2); public TestPipeline(String name, MuleContext muleContext) { super(name, muleContext); } @Override protected void configureMessageProcessors(MessageProcessorChainBuilder builder) throws MuleException { builder.chain((Processor) event -> { latch.countDown(); return event; }); super.configureMessageProcessors(builder); } @Override protected void configurePostProcessors(MessageProcessorChainBuilder builder) throws MuleException { super.configurePostProcessors(builder); builder.chain((Processor) event -> { latch.countDown(); return event; }); } @Override public String getConstructType() { return "test"; } } private class PipelineMessageNotificiationArgumentMatcher extends ArgumentMatcher<PipelineMessageNotification> { private int expectedAction; private boolean exceptionExpected; private Event event; public PipelineMessageNotificiationArgumentMatcher(int expectedAction, boolean exceptionExpected, Event event) { this.expectedAction = expectedAction; this.exceptionExpected = exceptionExpected; this.event = event; } @Override public boolean matches(Object argument) { EnrichedServerNotification notification = (EnrichedServerNotification) argument; MessagingException exception = null; if (notification instanceof PipelineMessageNotification) { exception = ((PipelineMessageNotification) notification).getException(); } else if (notification instanceof AsyncMessageNotification) { exception = ((AsyncMessageNotification) notification).getException(); } if (exceptionExpected) { return expectedAction == notification.getAction() && exception != null && notification.getMessage() != null && (this.event == null || this.event.getMessage().equals(notification.getMessage())); } else { return expectedAction == notification.getAction() && exception == null && notification.getMessage() != null && (this.event == null || this.event.getMessage().equals(notification.getMessage())); } } } public static class ExceptionThrowingMessageProcessor implements Processor { @Override public Event process(Event event) throws MuleException { throw new IllegalStateException(); } } }