/**
* Copyright 2014 Comcast Cable Communications Management, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.comcast.viper.flume2storm.spout;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import junit.framework.Assert;
import org.apache.commons.configuration.BaseConfiguration;
import org.apache.commons.configuration.Configuration;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import backtype.storm.Config;
import backtype.storm.ILocalCluster;
import backtype.storm.Testing;
import backtype.storm.testing.MkClusterParam;
import backtype.storm.testing.TestJob;
import backtype.storm.topology.TopologyBuilder;
import com.comcast.viper.flume2storm.F2SConfigurationException;
import com.comcast.viper.flume2storm.F2SEventSerializer;
import com.comcast.viper.flume2storm.connection.parameters.SimpleConnectionParameters;
import com.comcast.viper.flume2storm.connection.receptor.SimpleEventReceptorFactory;
import com.comcast.viper.flume2storm.connection.sender.SimpleEventSender;
import com.comcast.viper.flume2storm.event.F2SEvent;
import com.comcast.viper.flume2storm.event.F2SEventFactory;
import com.comcast.viper.flume2storm.location.SimpleLocationService;
import com.comcast.viper.flume2storm.location.SimpleLocationServiceFactory;
import com.comcast.viper.flume2storm.location.SimpleServiceProvider;
import com.comcast.viper.flume2storm.location.SimpleServiceProviderSerialization;
import com.comcast.viper.flume2storm.utility.test.TestCondition;
import com.comcast.viper.flume2storm.utility.test.TestUtils;
/**
* Test {@link FlumeSpout}
*/
public class FlumeSpoutTest {
protected static final Logger LOG = LoggerFactory.getLogger(FlumeSpoutTest.class);
protected static final int TEST_TIMEOUT = 120000;
private static final int BATCH_SIZE = 100;
private static final int NB_EVENTS = 150 * BATCH_SIZE;
private static Configuration flumeSpoutConfig;
private static Configuration eventSender1Config;
private static Configuration eventSender2Config;
protected SimpleLocationService locationService;
protected SimpleEventSender eventSender1;
protected SimpleEventSender eventSender2;
protected SortedSet<F2SEvent> eventsToSent;
@BeforeClass
public static void configure() {
// First Event Sender configuration
eventSender1Config = new BaseConfiguration();
eventSender1Config.addProperty(SimpleConnectionParameters.HOSTNAME, "localhost");
eventSender1Config.addProperty(SimpleConnectionParameters.PORT, 7001);
// Second Event Sender configuration
eventSender2Config = new BaseConfiguration();
eventSender2Config.addProperty(SimpleConnectionParameters.HOSTNAME, "localhost");
eventSender2Config.addProperty(SimpleConnectionParameters.PORT, 7002);
// Flume Spout configuration
flumeSpoutConfig = new BaseConfiguration();
flumeSpoutConfig.addProperty(FlumeSpoutConfiguration.LOCATION_SERVICE_FACTORY_CLASS,
SimpleLocationServiceFactory.class.getName());
flumeSpoutConfig.addProperty(FlumeSpoutConfiguration.SERVICE_PROVIDER_SERIALIZATION_CLASS,
SimpleServiceProviderSerialization.class.getName());
flumeSpoutConfig.addProperty(FlumeSpoutConfiguration.EVENT_RECEPTOR_FACTORY_CLASS,
SimpleEventReceptorFactory.class.getName());
}
@Before
public void setup() throws F2SConfigurationException {
// Creating and starting location service
locationService = new SimpleLocationServiceFactory().create(null, null);
locationService.start();
// Creating the KryoNet servers
eventSender1 = new SimpleEventSender(SimpleConnectionParameters.from(eventSender1Config));
eventSender1.start();
locationService.register(eventSender1);
eventSender2 = new SimpleEventSender(SimpleConnectionParameters.from(eventSender2Config));
eventSender2.start();
locationService.register(eventSender2);
// Generating random events
eventsToSent = F2SEventFactory.getInstance().generateRandomEvents(NB_EVENTS * 2);
}
@After
public void tearDown() throws Exception {
// Stopping even senders
locationService.unregister(eventSender1);
eventSender1.stop();
locationService.unregister(eventSender2);
eventSender2.stop();
// Stopping location service
locationService.stop();
}
/**
* @throws Exception
*/
@Test
public void testIt() throws Exception {
final MkClusterParam mkClusterParam = new MkClusterParam();
mkClusterParam.setSupervisors(2);
mkClusterParam.setPortsPerSupervisor(2);
Config daemonConf = new Config();
daemonConf.put(Config.STORM_LOCAL_MODE_ZMQ, false);
mkClusterParam.setDaemonConf(daemonConf);
Testing.withLocalCluster(new TestJob() {
@Override
public void run(final ILocalCluster cluster) throws Exception {
// Building the test topology
final TopologyBuilder builder = new TopologyBuilder();
Set<F2SEventEmitter> eventEmitters = new HashSet<F2SEventEmitter>();
eventEmitters.add(new BasicF2SEventEmitter());
FlumeSpout<SimpleConnectionParameters, SimpleServiceProvider> flumeSpout = new FlumeSpout<SimpleConnectionParameters, SimpleServiceProvider>(
eventEmitters, flumeSpoutConfig);
builder.setSpout("FlumeSpout", flumeSpout, 2);
final TestBolt psBolt = new TestBolt();
builder.setBolt("TestBolt", psBolt, 2).shuffleGrouping("FlumeSpout");
// Starting topology
final Config conf = new Config();
conf.setNumWorkers(4);
conf.registerSerialization(F2SEvent.class, F2SEventSerializer.class);
conf.setFallBackOnJavaSerialization(false);
cluster.submitTopology("TestTopology", conf, builder.createTopology());
Thread senderThread = new Thread(new Runnable() {
@Override
public void run() {
try {
// Waiting that topology is ready
LOG.info("Waiting that receptors connect...");
if (!TestUtils.waitFor(new TestCondition() {
@Override
public boolean evaluate() {
return eventSender1.getStats().getNbClients() == 2 && eventSender2.getStats().getNbClients() == 2;
}
}, TEST_TIMEOUT)) {
Assert.fail("Receptors failed to connect to senders in time (" + TEST_TIMEOUT + " ms)");
}
LOG.info("Receptors connected... sending events");
// Load balancing events between the 2 event senders
int batchNb = 0;
List<F2SEvent> batch = null;
for (F2SEvent event : eventsToSent) {
if (batch == null) {
batch = new ArrayList<F2SEvent>(BATCH_SIZE);
batchNb++;
}
batch.add(event);
if (batch.size() == BATCH_SIZE) {
SimpleEventSender eventSender = batchNb % 2 == 0 ? eventSender1 : eventSender2;
eventSender.send(batch);
batch = null;
}
}
LOG.info("Sent {} events", eventsToSent.size());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
senderThread.start();
// Waiting that it's done
if (!TestUtils.waitFor(new TestCondition() {
@Override
public boolean evaluate() {
LOG.debug("Received so far: " + MemoryStorage.getInstance().getReceivedEvents().size());
return MemoryStorage.getInstance().getReceivedEvents().size() >= NB_EVENTS * 2;
}
}, TEST_TIMEOUT)) {
Assert.fail("Failed to receive all events in time (" + TEST_TIMEOUT + " ms)");
}
// Testing results:
// 1 - Each sender have sent half of the events
assertThat(eventSender1.getStats().getNbEventsIn()).isEqualTo(NB_EVENTS);
assertThat(eventSender1.getStats().getNbEventsOut()).isEqualTo(NB_EVENTS);
assertThat(eventSender2.getStats().getNbEventsIn()).isEqualTo(NB_EVENTS);
assertThat(eventSender2.getStats().getNbEventsOut()).isEqualTo(NB_EVENTS);
// TODO 2 - Each spout have received half of the events
// 2 - We received all the events correctly
// Programming note: I used SortedSet and iterate over the 2 sets to
// speed up the comparison
assertThat(MemoryStorage.getInstance().getReceivedEvents().size()).isEqualTo(eventsToSent.size());
Iterator<F2SEvent> it1 = eventsToSent.iterator();
Iterator<F2SEvent> it2 = MemoryStorage.getInstance().getReceivedEvents().iterator();
while (it1.hasNext() && it2.hasNext()) {
assertThat(it1.next()).isEqualTo(it2.next());
}
}
});
}
}