/*
* Copyright 2014-2015 the original author or authors.
*
* 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 org.springframework.xd.distributed.test;
import static org.junit.Assert.assertEquals;
import java.io.File;
import java.io.FileReader;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.integration.test.util.SocketUtils;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.xd.rest.client.impl.SpringXDTemplate;
import org.springframework.xd.rest.domain.ModuleMetadataResource;
import org.springframework.xd.rest.domain.support.DeploymentPropertiesFormat;
import org.springframework.xd.test.rabbit.RabbitTestSupport;
import org.springframework.xd.test.redis.RedisTestSupport;
/**
* @author Patrick Peralta
* @author Mark Fisher
*/
public class StreamPartitionTests extends AbstractDistributedTests {
/**
* Enum for the various transport options used for partition testing.
*/
private enum Transport {
rabbit, redis
}
@Rule
public RabbitTestSupport rabbitAvailableRule = new RabbitTestSupport();
@Rule
public RedisTestSupport redisAvailableRule = new RedisTestSupport();
/**
* Test data partitioning using Redis as the transport.
*
* @throws Exception
* @see #testPartitioning
*/
@Test
public void testPartitioningWithRedis() throws Exception {
testPartitioning(Transport.redis);
}
/**
* Test data partitioning using Rabbit as the transport.
*
* @throws Exception
* @see #testPartitioning
*/
@Test
public void testPartitioningWithRabbit() throws Exception {
testPartitioning(Transport.rabbit);
}
/**
* Test data partitioning for the given transport. This method
* asserts that data sent through a source is correctly routed
* to the expected sinks based on the partitioning configuration.
*
* @param transport transport to use for partition testing
* @throws Exception
*/
private void testPartitioning(Transport transport) throws Exception {
Properties systemProperties = new Properties();
systemProperties.setProperty("xd.transport", transport.toString());
for (int i = 0; i < 2; i++) {
startContainer(systemProperties);
}
SpringXDTemplate template = ensureTemplate();
logger.info("Waiting for containers...");
waitForContainers();
logger.info("Containers running");
String streamName = testName.getMethodName() + "-woodchuck";
File file = File.createTempFile("temp", ".txt");
file.deleteOnExit();
int httpPort = SocketUtils.findAvailableServerSocket();
template.streamOperations().createStream(
streamName,
String.format("http --port=%s | splitter --expression=payload.split(' ') | " +
"file --dir=%s --name=${xd.container.id}", httpPort, file.getParent()), false);
verifyStreamCreated(streamName);
template.streamOperations().deploy(streamName, DeploymentPropertiesFormat.parseDeploymentProperties(
"module.splitter.producer.partitionKeyExpression=payload,module.file.count=2"));
// verify modules
Map<String, Properties> modules = new HashMap<String, Properties>();
int attempts = 0;
while (attempts++ < 60) {
Thread.sleep(500);
for (ModuleMetadataResource module : template.runtimeOperations().listDeployedModules()) {
modules.put(module.getContainerId() + ":" + module.getModuleType() + ":" + module.getName(),
module.getDeploymentProperties());
}
if (modules.size() == 4) {
break;
}
}
assertEquals("timed out waiting for stream modules to deploy", 4, modules.size());
RestTemplate restTemplate = new RestTemplate();
String text = "how much wood would a woodchuck chuck if a woodchuck could chuck wood";
int postAttempts = 0;
while (postAttempts++ < 50) {
Thread.sleep(100);
try {
ResponseEntity<?> entity = restTemplate.postForEntity("http://localhost:" + httpPort, text,
String.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
break;
}
catch (Exception e) {
// will try again
}
}
File[] outputFiles = new File[2];
for (Map.Entry<String, Properties> module : modules.entrySet()) {
if (module.getKey().contains("sink")) {
int index = Integer.parseInt(module.getValue().getProperty("consumer.partitionIndex"));
String container = module.getKey().substring(0, module.getKey().indexOf(':'));
File output = new File(file.getParent(), container + ".out");
output.deleteOnExit();
outputFiles[index] = output;
}
}
String expectedPartition0 = "how\nchuck\nchuck\n";
String expectedPartition1 = "much\nwood\nwould\na\nwoodchuck\nif\na\nwoodchuck\ncould\nwood\n";
int fileAttempts = 0;
String[] results = new String[2];
while (fileAttempts++ < 10) {
Thread.sleep(500);
if (outputFiles[0].exists() && outputFiles[1].exists()) {
results[0] = FileCopyUtils.copyToString(new FileReader(outputFiles[0]));
results[1] = FileCopyUtils.copyToString(new FileReader(outputFiles[1]));
if (results[0].length() == expectedPartition0.length()
&& results[1].length() == expectedPartition1.length()) {
break;
}
}
}
assertEquals(expectedPartition0, results[0]);
assertEquals(expectedPartition1, results[1]);
}
}