/*
* 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.routing;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mule.runtime.api.message.Message.of;
import static org.mule.tck.MuleTestUtils.getTestFlow;
import org.mule.runtime.api.exception.MuleException;
import org.mule.runtime.core.api.Event;
import org.mule.runtime.core.api.construct.Flow;
import org.mule.runtime.core.api.processor.Processor;
import org.mule.runtime.core.api.store.ListableObjectStore;
import org.mule.runtime.core.internal.construct.DefaultFlowBuilder;
import org.mule.runtime.core.util.store.SimpleMemoryObjectStore;
import org.mule.tck.junit4.AbstractMuleContextTestCase;
import org.mule.tck.probe.JUnitProbe;
import org.mule.tck.probe.PollingProber;
import org.mule.tck.probe.Prober;
import java.io.ByteArrayInputStream;
import org.junit.Test;
public class UntilSuccessfulTestCase extends AbstractMuleContextTestCase {
public static class ConfigurableMessageProcessor implements Processor {
private volatile int eventCount;
private volatile Event event;
private volatile int numberOfFailuresToSimulate;
@Override
public Event process(final Event evt) throws MuleException {
eventCount++;
if (numberOfFailuresToSimulate-- > 0) {
throw new RuntimeException("simulated problem");
}
this.event = evt;
return evt;
}
public Event getEventReceived() {
return event;
}
public int getEventCount() {
return eventCount;
}
public void setNumberOfFailuresToSimulate(int numberOfFailuresToSimulate) {
this.numberOfFailuresToSimulate = numberOfFailuresToSimulate;
}
}
private UntilSuccessful untilSuccessful;
private ListableObjectStore<Event> objectStore;
private ConfigurableMessageProcessor targetMessageProcessor;
private Prober pollingProber = new PollingProber(10000, 500l);
private Flow mockFlow;
@Override
protected void doSetUp() throws Exception {
super.doSetUp();
untilSuccessful = buildUntiSuccessful(1000L);
mockFlow = mock(DefaultFlowBuilder.DefaultFlow.class);
}
private UntilSuccessful buildUntiSuccessful(Long millisBetweenRetries) throws Exception {
UntilSuccessful untilSuccessful = new UntilSuccessful();
untilSuccessful.setMuleContext(muleContext);
untilSuccessful.setMessagingExceptionHandler(muleContext.getDefaultErrorHandler());
untilSuccessful.setFlowConstruct(getTestFlow(muleContext));
untilSuccessful.setMaxRetries(2);
if (millisBetweenRetries != null) {
untilSuccessful.setMillisBetweenRetries(millisBetweenRetries);
}
objectStore = new SimpleMemoryObjectStore<>();
untilSuccessful.setObjectStore(objectStore);
targetMessageProcessor = new ConfigurableMessageProcessor();
untilSuccessful.addRoute(targetMessageProcessor);
return untilSuccessful;
}
@Override
protected void doTearDown() throws Exception {
untilSuccessful.stop();
}
@Test
public void testSuccessfulDelivery() throws Exception {
untilSuccessful.initialise();
untilSuccessful.start();
assertSame(testEvent(), untilSuccessful.process(testEvent()));
ponderUntilEventProcessed(testEvent());
}
@Test
public void testSuccessfulDeliveryStreamPayload() throws Exception {
untilSuccessful.setMuleContext(muleContext);
untilSuccessful.initialise();
untilSuccessful.start();
final Event testEvent = eventBuilder().message(of(new ByteArrayInputStream("test_data".getBytes()))).build();
assertSame(testEvent, untilSuccessful.process(testEvent));
ponderUntilEventProcessed(testEvent);
}
@Test
public void testSuccessfulDeliveryAckExpression() throws Exception {
untilSuccessful.setAckExpression("#[mel:'ACK']");
untilSuccessful.setMuleContext(muleContext);
untilSuccessful.setFlowConstruct(mockFlow);
untilSuccessful.initialise();
untilSuccessful.start();
assertThat(untilSuccessful.process(testEvent()).getMessageAsString(muleContext), equalTo("ACK"));
waitDelivery();
}
@Test
public void testSuccessfulDeliveryFailureExpression() throws Exception {
untilSuccessful.setFailureExpression("#[mel:regex('(?i)error')]");
untilSuccessful.setMuleContext(muleContext);
untilSuccessful.initialise();
untilSuccessful.start();
assertSame(testEvent(), untilSuccessful.process(testEvent()));
ponderUntilEventProcessed(testEvent());
}
@Test
public void testPermanentDeliveryFailure() throws Exception {
targetMessageProcessor.setNumberOfFailuresToSimulate(Integer.MAX_VALUE);
untilSuccessful.setMuleContext(muleContext);
untilSuccessful.initialise();
untilSuccessful.start();
final Event testEvent = eventBuilder().message(of("ERROR")).build();
assertSame(testEvent, untilSuccessful.process(testEvent));
ponderUntilEventAborted(testEvent);
}
@Test
public void testPermanentDeliveryFailureExpression() throws Exception {
untilSuccessful.setFailureExpression("#[mel:regex('(?i)error')]");
untilSuccessful.setMuleContext(muleContext);
untilSuccessful.initialise();
untilSuccessful.start();
final Event testEvent = eventBuilder().message(of("ERROR")).build();
assertSame(testEvent, untilSuccessful.process(testEvent));
ponderUntilEventAborted(testEvent);
}
@Test
public void testTemporaryDeliveryFailure() throws Exception {
targetMessageProcessor.setNumberOfFailuresToSimulate(untilSuccessful.getMaxRetries());
untilSuccessful.setMuleContext(muleContext);
untilSuccessful.initialise();
untilSuccessful.start();
final Event testEvent = eventBuilder().message(of("ERROR")).build();
assertSame(testEvent, untilSuccessful.process(testEvent));
ponderUntilEventProcessed(testEvent);
assertEquals(targetMessageProcessor.getEventCount(), untilSuccessful.getMaxRetries() + 1);
}
@Test
public void testPreExistingEvents() throws Exception {
objectStore.store(new AsynchronousUntilSuccessfulProcessingStrategy().buildQueueKey(testEvent(), getTestFlow(muleContext),
muleContext),
testEvent());
untilSuccessful.setMuleContext(muleContext);
untilSuccessful.initialise();
untilSuccessful.start();
ponderUntilEventProcessed(testEvent());
}
@Test
public void testDefaultMillisWait() throws Exception {
untilSuccessful = buildUntiSuccessful(null);
untilSuccessful.setMuleContext(muleContext);
untilSuccessful.initialise();
untilSuccessful.start();
assertEquals(60 * 1000, untilSuccessful.getMillisBetweenRetries());
}
@Test
public void testMillisWait() throws Exception {
final long millis = 10;
untilSuccessful.setMillisBetweenRetries(millis);
untilSuccessful.setMuleContext(muleContext);
untilSuccessful.initialise();
untilSuccessful.start();
assertEquals(millis, untilSuccessful.getMillisBetweenRetries());
}
@Test
public void testSecondsWait() throws Exception {
final long seconds = 10;
untilSuccessful = buildUntiSuccessful(null);
untilSuccessful.setSecondsBetweenRetries(seconds);
untilSuccessful.setMuleContext(muleContext);
untilSuccessful.initialise();
untilSuccessful.start();
assertEquals(seconds * 1000, untilSuccessful.getMillisBetweenRetries());
}
@Test(expected = IllegalArgumentException.class)
public void testMillisAndSecondsWait() throws Exception {
untilSuccessful.setMillisBetweenRetries(1000L);
untilSuccessful.setSecondsBetweenRetries(1000);
untilSuccessful.initialise();
}
private void ponderUntilEventProcessed(final Event testEvent) throws InterruptedException, MuleException {
waitDelivery();
assertLogicallyEqualEvents(testEvent, targetMessageProcessor.getEventReceived());
}
private void waitDelivery() {
pollingProber.check(new JUnitProbe() {
@Override
protected boolean test() throws Exception {
return targetMessageProcessor.getEventReceived() != null && objectStore.allKeys().isEmpty();
}
@Override
public String describeFailure() {
return "Event not received by target";
}
});
}
private void ponderUntilEventAborted(final Event testEvent) throws InterruptedException, MuleException {
pollingProber.check(new JUnitProbe() {
@Override
protected boolean test() throws Exception {
return targetMessageProcessor.getEventCount() > untilSuccessful.getMaxRetries() && objectStore.allKeys().isEmpty();
}
@Override
public String describeFailure() {
return String.format("Processing not retried %s times.", untilSuccessful.getMaxRetries());
}
});
assertEquals(0, objectStore.allKeys().size());
assertEquals(targetMessageProcessor.getEventCount(), 1 + untilSuccessful.getMaxRetries());
}
private void assertLogicallyEqualEvents(final Event testEvent, Event eventReceived) throws MuleException {
// events have been rewritten so are different but the correlation ID has been carried around
assertEquals(testEvent.getCorrelationId(), eventReceived.getCorrelationId());
// and their payload
assertEquals(testEvent.getMessageAsString(muleContext), eventReceived.getMessageAsString(muleContext));
}
}