/*
* Copyright 2013 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.dirt.stream;
import static org.junit.Assert.assertTrue;
import java.util.List;
import java.util.Map;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.SubscribableChannel;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.util.Assert;
import org.springframework.xd.dirt.module.ResourceModuleRegistry;
import org.springframework.xd.dirt.module.WritableModuleRegistry;
import org.springframework.xd.dirt.server.TestApplicationBootstrap;
import org.springframework.xd.dirt.server.singlenode.SingleNodeApplication;
import org.springframework.xd.dirt.test.SingleNodeIntegrationTestSupport;
import org.springframework.xd.module.core.CompositeModule;
import org.springframework.xd.module.core.Module;
/**
* @author David Turanski
*/
public class StreamTestSupport {
private static WritableModuleRegistry moduleRegistry;
private static SingleNodeApplication application;
private static ConfigurableApplicationContext adminContext;
private static SingleNodeIntegrationTestSupport integrationTestSupport;
@BeforeClass
public static void startXDSingleNode() throws Exception {
application = new TestApplicationBootstrap().getSingleNodeApplication().run("--analytics", "memory");
// Explicitly set this to true since RandomConfigurationSupport disables JMX by default.
System.setProperty("XD_JMX_ENABLED", "true");
adminContext = application.adminContext();
moduleRegistry = adminContext.getBean(WritableModuleRegistry.class);
integrationTestSupport = new SingleNodeIntegrationTestSupport(application);
integrationTestSupport.addModuleRegistry(new ResourceModuleRegistry("classpath:/testmodules/"));
}
protected static boolean deployStream(String name, String config) {
return integrationTestSupport.createAndDeployStream(new StreamDefinition(name, config));
}
protected static void undeployStream(String name) {
StreamDefinition sd = integrationTestSupport.streamDefinitionRepository().findOne(name);
integrationTestSupport.undeployStream(sd);
}
protected static void deleteStream(String name) {
integrationTestSupport.deleteStream(name);
}
protected static Module getDeployedModule(String streamName, int index) {
Map<Integer, Module> streamModules = getStreamModules(streamName);
return streamModules.get(index);
}
protected static Module getDeployedSource(String streamName) {
Map<Integer, Module> streamModules = getStreamModules(streamName);
return streamModules.get(0);
}
protected static Module getDeployedSink(String streamName) {
Map<Integer, Module> streamModules = getStreamModules(streamName);
return streamModules.get(streamModules.size() - 1);
}
protected static Map<Integer, Module> getStreamModules(String streamName) {
Map<String, Map<Integer, Module>> deployedModules = integrationTestSupport.getDeployedModules();
Assert.notNull(deployedModules.get(streamName), "Stream '" + streamName
+ "' apparently is not deployed. Deployed modules: " + deployedModules);
return deployedModules.get(streamName);
}
protected static MessageChannel getSourceOutputChannel(String streamName) {
Module source = getDeployedSource(streamName);
if (source instanceof CompositeModule) {
source = (Module) TestUtils.getPropertyValue(source, "modules", List.class).get(0);
}
return source.getComponent("output", MessageChannel.class);
}
protected static SubscribableChannel getSinkInputChannel(String streamName) {
Module sink = getDeployedSink(streamName);
// Should be a publish-subscribe-channel
if (sink instanceof CompositeModule) {
@SuppressWarnings("unchecked")
List<Module> modules = TestUtils.getPropertyValue(sink, "modules", List.class);
sink = modules.get(modules.size() - 1);
}
return sink.getComponent("input", SubscribableChannel.class);
}
protected static ConfigurableApplicationContext getAdminContext() {
return adminContext;
}
protected static WritableModuleRegistry getModuleRegistry() {
return moduleRegistry;
}
@AfterClass
public static void cleanUp() {
if (application != null) {
application.close();
}
}
protected void sendMessageAndVerifyOutput(String streamName, Message<?> message, MessageTest test) {
Assert.notNull(streamName, "streamName cannot be null");
Assert.notNull(test, "test cannot be null");
Assert.notNull(message, "message cannot be null");
MessageChannel producer = getSourceOutputChannel(streamName);
SubscribableChannel consumer = getSinkInputChannel(streamName);
consumer.subscribe(test);
producer.send(message);
assertTrue(test.getMessageHandled());
}
protected void sendPayloadAndVerifyOutput(String streamName, Object payload, MessageTest test) {
Assert.notNull(payload, "payload cannot be null");
sendMessageAndVerifyOutput(streamName, new GenericMessage<Object>(payload), test);
}
protected void sendPayloadAndVerifyTappedOutput(String streamName, Object payload, String moduleToTap,
MessageTest test) {
Assert.notNull(payload, "payload cannot be null");
sendMessageAndVerifyTappedOutput(streamName, new GenericMessage<Object>(payload), moduleToTap, test);
}
protected void sendMessageAndVerifyTappedOutput(String streamName, Message<?> message, String moduleToTap,
MessageTest test) {
Assert.notNull(streamName, "streamName cannot be null");
Assert.notNull(test, "test cannot be null");
Assert.notNull(message, "message cannot be null");
String tapName = streamName + "Tap";
String tapChannel = "tap:stream:" + streamName;
if (moduleToTap != null) {
tapChannel = tapChannel + "." + moduleToTap;
}
deployStream(
tapName,
tapChannel + " > sink");
MessageChannel producer = getSourceOutputChannel(streamName);
SubscribableChannel consumer = getSinkInputChannel(tapName);
SubscribableChannel streamConsumer = getSinkInputChannel(streamName);
// Add a dummy consumer to the stream in case there is none
streamConsumer.subscribe(new MessageHandler() {
@Override
public void handleMessage(Message<?> message) throws MessagingException {
}
});
consumer.subscribe(test);
producer.send(message);
test.waitForCompletion(10000);
assertTrue(test.getMessageHandled());
undeployStream(tapName);
}
protected static abstract class MessageTest implements MessageHandler {
protected boolean messageHandled;
public boolean getMessageHandled() {
return this.messageHandled;
}
@Override
public final void handleMessage(Message<?> message) throws MessagingException {
this.test(message);
messageHandled = true;
}
protected abstract void test(Message<?> message);
protected void waitForCompletion(int maxtime) {
int time = 0;
while (time < maxtime && !getMessageHandled()) {
try {
Thread.sleep(100);
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IllegalStateException("interrupted while waiting for completion", e);
}
time += 100;
}
}
}
}