/* # Licensed Materials - Property of IBM # Copyright IBM Corp. 2015 */ package com.ibm.streamsx.topology.test.distributed; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; import org.junit.Before; import org.junit.Test; import com.ibm.json.java.JSONObject; import com.ibm.streams.operator.types.Blob; import com.ibm.streams.operator.types.ValueFactory; import com.ibm.streams.operator.types.XML; import com.ibm.streamsx.topology.TStream; import com.ibm.streamsx.topology.Topology; import com.ibm.streamsx.topology.context.StreamsContext.Type; import com.ibm.streamsx.topology.spl.SPLSchemas; import com.ibm.streamsx.topology.spl.SPLStream; import com.ibm.streamsx.topology.spl.SPLStreams; import com.ibm.streamsx.topology.streams.StringStreams; import com.ibm.streamsx.topology.test.TestTopology; import com.ibm.streamsx.topology.tester.Condition; /** * Test publish/subscribe. These tests just use publish/subscribe * within a single job, but the expected use case is across jobs. * */ public class PublishSubscribeTest extends TestTopology { @Before public void checkIsDistributed() { assumeTrue(getTesterType() == Type.DISTRIBUTED_TESTER); } @Test public void testPublishString() throws Exception { TStream<String> source = source(); source.publish("testPublishString"); TStream<String> subscribe = source.topology().subscribe("testPublishString", String.class); checkSubscribedAsStrings(subscribe); } private TStream<String> source() { return source("S"); } private TStream<String> source(String prefix) { final Topology t = new Topology(); int[] sv = new int[1]; TStream<String> source = t.periodicSource(() -> prefix + sv[0]++, 50, TimeUnit.MILLISECONDS); return source.asType(String.class); } @Test public void testPublishStringMultipleTopics() throws Exception { TStream<String> source = source(); source.publish("testPublishString"); // A stream that should not be subscribed to! TStream<String> source2 = source("X"); source2.publish("testPublishString2"); TStream<String> subscribe = source.topology().subscribe("testPublishString", String.class); checkSubscribedAsStrings(subscribe); } // Test a exported String Java stream can be subscribed to by a SPL stream. @Test public void testPublishStringToSPL() throws Exception { TStream<String> source = source(); // Check autonomous works in that it produces working SPL code. source = source.autonomous(); assertEquals(String.class, source.getTupleClass()); assertEquals(String.class, source.getTupleType()); source.publish("testPublishStringSPL"); SPLStream subscribe = SPLStreams.subscribe(source.topology(), "testPublishStringSPL", SPLSchemas.STRING); checkSubscribedAsStrings(subscribe.toStringStream()); } @Test public void testPublishBlob() throws Exception { TStream<Blob> blobs = source().transform( v -> ValueFactory.newBlob(v.getBytes(StandardCharsets.UTF_8))).asType(Blob.class); blobs.publish("testPublishBlob"); TStream<Blob> subscribe = blobs.topology().subscribe("testPublishBlob", Blob.class); TStream<String> strings = subscribe.transform(v -> new String(v.getData(), StandardCharsets.UTF_8)); checkSubscribedAsStrings(strings); } private void checkSubscribedAsStrings(TStream<String> strings) throws Exception { Topology t = strings.topology(); Condition<Long> atLeast = t.getTester().atLeastTupleCount(strings, 100); Condition<List<String>> subTuples = t.getTester().stringContents(strings); complete(t.getTester(), atLeast, 30, TimeUnit.SECONDS); assertTrue(atLeast.valid()); List<String> result = subTuples.getResult(); assertFalse(result.isEmpty()); int last = -1; for (String r : result) { assertEquals('S', r.charAt(0)); int v = Integer.valueOf(r.substring(1)); if (last != -1) assertEquals(last+1, v); last = v; } } @Test public void testPublishXML() throws Exception { TStream<String> source = source(); source = source.transform( s -> "<a>" + s + "</a>"); TStream<XML> xml = source.transform( v -> { try { return ValueFactory.newXML(new ByteArrayInputStream(v.getBytes(StandardCharsets.UTF_8))); } catch (IOException e) { return null; } }).asType(XML.class); xml.publish("testPublishXML"); TStream<XML> subscribe = source.topology().subscribe("testPublishXML", XML.class); TStream<String> strings = subscribe.transform(v-> { byte[] data = new byte[100]; InputStream in = v.getInputStream(); int read; try { read = in.read(data); } catch (IOException e) { return null; } return new String(data, 0, read, StandardCharsets.UTF_8); }); strings = strings.transform(s -> s.substring(3, s.length() - 4)); checkSubscribedAsStrings(strings); } @Test public void testPublishJavaObject() throws Exception { TStream<String> source = source(); TStream<SimpleString> objects = source.transform(SimpleString::new).asType(SimpleString.class); objects.publish("testPublishJavaObject"); TStream<SimpleString> subscribe = source.topology().subscribe("testPublishJavaObject", SimpleString.class); TStream<String> strings = StringStreams.toString(subscribe); checkSubscribedAsStrings(strings); } /** * Publish two Java object streams to the same topic * but ensure that the subscriber selects the correct one * based upon type. */ public TStream<String> publishJavaObjectMultiple() throws Exception { final Topology t = new Topology(); List<SimpleInt> ints = new ArrayList<>(); ints.add(new SimpleInt(0)); ints.add(new SimpleInt(1)); ints.add(new SimpleInt(2)); ints.add(new SimpleInt(3)); TStream<SimpleInt> sints = t.constants(ints).asType(SimpleInt.class); sints = addStartupDelay(sints); sints.publish("testPublishJavaObjects"); TStream<String> source = t.strings("publishing", "a", "java object"); source = addStartupDelay(source); TStream<SimpleString> objects = source.transform(SimpleString::new).asType(SimpleString.class); objects.publish("testPublishJavaObjects"); TStream<SimpleString> subscribe = t.subscribe("testPublishJavaObjects", SimpleString.class); TStream<String> strings = StringStreams.toString(subscribe); return strings; } @SuppressWarnings("serial") public static class SimpleString implements Serializable { private final String value; public SimpleString(String value) { this.value = value; } @Override public String toString() { return value; } } @SuppressWarnings("serial") public static class SimpleInt implements Serializable { private final int value; public SimpleInt(int value) { this.value = value; } @Override public String toString() { return "SimpleInt:" + value; } } @Test(expected=IllegalArgumentException.class) public void testFilterOnJava() throws Exception { final Topology t = new Topology(); TStream<Integer> ints = t.constants(Collections.<Integer>emptyList()).asType(Integer.class); ints.publish("sometopic", true); } @Test(expected=IllegalArgumentException.class) public void testFilterOnXML() throws Exception { final Topology t = new Topology(); TStream<XML> xmls = t.constants(Collections.<XML>emptyList()).asType(XML.class);; xmls.publish("sometopic", true); } @Test(expected=IllegalArgumentException.class) public void testFilterOnBlob() throws Exception { final Topology t = new Topology(); TStream<Blob> blobs = t.constants(Collections.<Blob>emptyList()).asType(Blob.class); blobs.publish("sometopic", true); } @Test(expected=IllegalArgumentException.class) public void testFilterOnJson() throws Exception { final Topology t = new Topology(); TStream<JSONObject> json = t.constants(Collections.<JSONObject>emptyList()).asType(JSONObject.class);; json.publish("sometopic", true); } }