/* * Copyright 2015 Kaazing Corporation * * 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 io.aeron.samples; import io.aeron.Aeron; import io.aeron.FragmentAssembler; import io.aeron.Image; import io.aeron.Subscription; import io.aeron.logbuffer.FragmentHandler; import org.agrona.concurrent.BackoffIdleStrategy; import org.agrona.concurrent.IdleStrategy; import org.agrona.concurrent.SigInt; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; /** * A subscriber application with two subscriptions which can receive fragmented messages * Creates two subscriptions on a given channel subscribed to two different stream IDs. * The default STREAM_ID and CHANNEL are configured in {@link SampleConfiguration}. The default * channel and stream IDs can be changed by setting Java system properties at the command line, e.g.: * -Daeron.sample.channel=aeron:udp?endpoint=localhost:5555 -Daeron.sample.streamId=20 */ public class MultipleSubscribersWithFragmentAssembly { private static final int FRAGMENT_COUNT_LIMIT = SampleConfiguration.FRAGMENT_COUNT_LIMIT; private static final int STREAM_ID_1 = SampleConfiguration.STREAM_ID; private static final int STREAM_ID_2 = SampleConfiguration.STREAM_ID + 1; private static final String CHANNEL = SampleConfiguration.CHANNEL; public static void main(final String[] args) throws Exception { System.out.format("Subscribing to %s on stream ID %d and stream ID %d%n", CHANNEL, STREAM_ID_1, STREAM_ID_2); final Aeron.Context ctx = new Aeron.Context() .availableImageHandler(MultipleSubscribersWithFragmentAssembly::eventAvailableImage) .unavailableImageHandler(MultipleSubscribersWithFragmentAssembly::eventUnavailableImage); final FragmentAssembler dataHandler1 = new FragmentAssembler(reassembledStringMessage1(STREAM_ID_1)); final FragmentAssembler dataHandler2 = new FragmentAssembler(reassembledStringMessage2(STREAM_ID_2)); final AtomicBoolean running = new AtomicBoolean(true); SigInt.register(() -> running.set(false)); try (Aeron aeron = Aeron.connect(ctx); Subscription subscription1 = aeron.addSubscription(CHANNEL, STREAM_ID_1); Subscription subscription2 = aeron.addSubscription(CHANNEL, STREAM_ID_2)) { final IdleStrategy idleStrategy = new BackoffIdleStrategy( 100, 10, TimeUnit.MICROSECONDS.toNanos(1), TimeUnit.MICROSECONDS.toNanos(100)); int idleCount = 0; while (running.get()) { final int fragmentsRead1 = subscription1.poll(dataHandler1, FRAGMENT_COUNT_LIMIT); final int fragmentsRead2 = subscription2.poll(dataHandler2, FRAGMENT_COUNT_LIMIT); if ((fragmentsRead1 + fragmentsRead2) == 0) { idleStrategy.idle(idleCount++); } else { idleCount = 0; } } System.out.println("Shutting down..."); } } /** * Print the information for an available image to stdout. * * @param image that has been created */ public static void eventAvailableImage(final Image image) { final Subscription subscription = image.subscription(); System.out.format( "new image on %s streamId %x sessionId %x from %s%n", subscription.channel(), subscription.streamId(), image.sessionId(), image.sourceIdentity()); } /** * This handler is called when image is unavailable * * @param image that has gone inactive */ public static void eventUnavailableImage(final Image image) { final Subscription subscription = image.subscription(); System.out.format( "inactive image on %s streamId %d sessionId %x%n", subscription.channel(), subscription.streamId(), image.sessionId()); } /** * Return a reusable, parameterized {@link FragmentHandler} that prints to stdout for the first stream(STREAM) * * @param streamId to show when printing * @return subscription data handler function that prints the message contents * @throws Exception if an error occurs */ public static FragmentHandler reassembledStringMessage1(final int streamId) throws Exception { return (buffer, offset, length, header) -> { final byte[] data = new byte[length]; buffer.getBytes(offset, data); System.out.format( "message to stream %d from session %x term id %x term offset %d (%d@%d)%n", streamId, header.sessionId(), header.termId(), header.termOffset(), length, offset); if (length != 10000) { System.out.format( "Received message was not assembled properly;" + " received length was %d, but was expecting 10000%n", length); } }; } /** * Return a reusable, parameterized {@link FragmentHandler} that prints to stdout for the second stream (STREAM + 1) * * @param streamId to show when printing * @return subscription data handler function that prints the message contents * @throws Exception if an error occurs */ public static FragmentHandler reassembledStringMessage2(final int streamId) throws Exception { return (buffer, offset, length, header) -> { final byte[] data = new byte[length]; buffer.getBytes(offset, data); System.out.format( "message to stream %d from session %x term id %x term offset %d (%d@%d)%n", streamId, header.sessionId(), header.termId(), header.termOffset(), length, offset); if (length != 9000) { System.out.format( "Received message was not assembled properly; received length was %d, but was expecting 9000%n", length); } }; } }