/*
* 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 java.util.Collections.singletonList;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
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.api.message.Message;
import org.mule.runtime.core.DefaultEventContext;
import org.mule.runtime.core.api.DefaultMuleException;
import org.mule.runtime.core.api.Event;
import org.mule.runtime.core.api.MuleSession;
import org.mule.runtime.core.api.construct.FlowConstruct;
import org.mule.runtime.core.api.processor.Processor;
import org.mule.runtime.core.session.DefaultMuleSession;
import org.mule.tck.junit4.AbstractMuleContextTestCase;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.Test;
public class RoundRobinTestCase extends AbstractMuleContextTestCase {
private final static int NUMBER_OF_ROUTES = 10;
private final static int NUMBER_OF_MESSAGES = 10;
private final AtomicInteger messageNumber = new AtomicInteger(0);
public RoundRobinTestCase() {
setStartContext(true);
}
@Test
public void testRoundRobin() throws Exception {
RoundRobin rr = new RoundRobin();
rr.setMuleContext(muleContext);
MuleSession session = new DefaultMuleSession();
List<TestProcessor> routes = new ArrayList<>(NUMBER_OF_ROUTES);
for (int i = 0; i < NUMBER_OF_ROUTES; i++) {
routes.add(new TestProcessor());
}
rr.setRoutes(new ArrayList<Processor>(routes));
List<Thread> threads = new ArrayList<>(NUMBER_OF_ROUTES);
for (int i = 0; i < NUMBER_OF_ROUTES; i++) {
threads.add(new Thread(new TestDriver(session, rr, NUMBER_OF_MESSAGES, getTestFlow(muleContext))));
}
for (Thread t : threads) {
t.start();
}
for (Thread t : threads) {
t.join();
}
for (TestProcessor route : routes) {
assertEquals(NUMBER_OF_MESSAGES, route.getCount());
}
}
@Test
public void usesFirstRouteOnFirstRequest() throws Exception {
RoundRobin roundRobin = new RoundRobin();
roundRobin.setMuleContext(muleContext);
List<Processor> routes = new ArrayList<>(2);
Processor route1 = mock(Processor.class, "route1");
routes.add(route1);
Processor route2 = mock(Processor.class, "route2");
routes.add(route2);
roundRobin.setRoutes(new ArrayList<>(routes));
Message message = of(singletonList(TEST_MESSAGE));
roundRobin.process(eventBuilder().message(message).build());
verify(route1).process(any(Event.class));
verify(route2, never()).process(any(Event.class));
}
class TestDriver implements Runnable {
private Processor target;
private int numMessages;
private MuleSession session;
private FlowConstruct flowConstruct;
TestDriver(MuleSession session, Processor target, int numMessages, FlowConstruct flowConstruct) {
this.target = target;
this.numMessages = numMessages;
this.session = session;
this.flowConstruct = flowConstruct;
}
@Override
public void run() {
for (int i = 0; i < numMessages; i++) {
Message msg = of(TEST_MESSAGE + messageNumber.getAndIncrement());
Event event = Event.builder(DefaultEventContext.create(flowConstruct, TEST_CONNECTOR_LOCATION)).message(msg)
.flow(flowConstruct).session(session).build();
try {
target.process(event);
} catch (MuleException e) {
// this is expected
}
}
}
}
static class TestProcessor implements Processor {
private int count;
private List<Object> payloads = new ArrayList<>();
@Override
public Event process(Event event) throws MuleException {
payloads.add(event.getMessage().getPayload().getValue());
count++;
if (count % 3 == 0) {
throw new DefaultMuleException("Mule Exception!");
}
return null;
}
public int getCount() {
return count;
}
}
}