/** * GRANITE DATA SERVICES * Copyright (C) 2006-2015 GRANITE DATA SERVICES S.A.S. * * This file is part of the Granite Data Services Platform. * * Granite Data Services is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * Granite Data Services is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA, or see <http://www.gnu.org/licenses/>. */ package org.granite.client.test.server; import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.granite.client.messaging.Consumer; import org.granite.client.messaging.ResultIssuesResponseListener; import org.granite.client.messaging.ServerApp; import org.granite.client.messaging.TopicMessageListener; import org.granite.client.messaging.channel.ChannelFactory; import org.granite.client.messaging.channel.MessagingChannel; import org.granite.client.messaging.events.IssueEvent; import org.granite.client.messaging.events.ResultEvent; import org.granite.client.messaging.events.TopicMessageEvent; import org.granite.client.test.server.feed.FeedApplication; import org.granite.client.test.server.feed.FeedListener; import org.granite.client.test.server.feed.Info; import org.granite.logging.Logger; import org.granite.test.container.EmbeddedContainer; import org.granite.util.ContentType; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.spec.WebArchive; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; /** * Created by william on 30/09/13. */ @RunWith(Parameterized.class) public class TestMessagingReconnect { private static final Logger log = Logger.getLogger(TestMessagingReconnect.class); private static final String APP_NAME = "feed"; @Parameterized.Parameters(name = "container: {0}, encoding: {1}, channel: {2}") public static Iterable<Object[]> data() { return ContainerTestUtil.data(); } private ContentType contentType; private String channelType; protected static EmbeddedContainer container; private static final ServerApp SERVER_APP = new ServerApp("/" + APP_NAME, false, "localhost", 8787); public TestMessagingReconnect(String containerClassName, ContentType contentType, String channelType) { this.contentType = contentType; this.channelType = channelType; } @BeforeClass public static void startContainer() throws Exception { // Build a feed server application WebArchive war = ShrinkWrap.create(WebArchive.class, APP_NAME + ".war"); war.addClasses(FeedApplication.class, FeedListener.class, Info.class); war.addAsWebInfResource(new File("granite-client-java/src/test/resources/granite-config-server-reconnect.xml"), "granite/granite-config.xml"); container = ContainerTestUtil.newContainer(war, true); container.start(); log.info("Container started"); } @AfterClass public static void stopContainer() throws Exception { container.stop(); container.destroy(); log.info("Container stopped"); } @Test public void testFeedSingleConsumerReconnect() throws Exception { log.info("TestMessagingReconnect.testFeedSingleConsumerReconnect %s - %s", channelType, contentType); CyclicBarrier[] barriers = new CyclicBarrier[4]; barriers[0] = new CyclicBarrier(2); barriers[1] = new CyclicBarrier(2); barriers[2] = new CyclicBarrier(2); barriers[3] = new CyclicBarrier(2); ConsumerThread consumer = new ConsumerThread("C", barriers); consumer.start(); try { barriers[0].await(5, TimeUnit.SECONDS); } catch (TimeoutException e) { log.error(e, "Consumer subscription timeout"); Assert.fail("Consumer subscription failed"); } try { barriers[1].await(10, TimeUnit.SECONDS); } catch (TimeoutException e) { log.error(e, "Consumer reception before restart timeout"); Assert.fail("Consumer receive messages before restart failed"); } container.restart(); try { barriers[2].await(50, TimeUnit.SECONDS); } catch (TimeoutException e) { log.error(e, "Consumer reception after restart timeout"); Assert.fail("Consumer receive messages after restart failed"); } try { barriers[3].await(5, TimeUnit.SECONDS); } catch (TimeoutException e) { log.error(e, "Consumer unsubscription timeout"); Assert.fail("Consumer unsubscription failed"); } } private class ConsumerThread implements Runnable { private String id; private List<Info> received = new ArrayList<Info>(); private CyclicBarrier[] barriers; private Thread thread = new Thread(this); private ChannelFactory channelFactory; private Consumer consumer; public ConsumerThread(String id, CyclicBarrier[] barriers) { this.id = id; thread.setName(id); this.barriers = barriers; } public void start() { thread.start(); } private CountDownLatch waitToStop = new CountDownLatch(1); @Override public void run() { channelFactory = ContainerTestUtil.buildChannelFactory(contentType); final MessagingChannel channel = channelFactory.newMessagingChannel(channelType, "messagingamf", SERVER_APP); consumer = new Consumer(channel, "feed", "feed"); consumer.addMessageListener(new ConsumerMessageListener()); consumer.subscribe(new ResultIssuesResponseListener() { @Override public void onResult(ResultEvent event) { log.info("Consumer %s: subscribed %s", id, channel.getClientId()); try { barriers[0].await(); } catch (Exception e) { } } @Override public void onIssue(IssueEvent event) { log.error("Consumer %s: subscription failed %s", id, event.toString()); } }); try { if (!waitToStop.await(20, TimeUnit.SECONDS)) log.error("Consumer %s time out", id); } catch (Exception e) { log.error(e, "Consumer %s interrupted", id); } try { channelFactory.stop(); barriers[3].await(); } catch (Exception e) { log.error(e, "Consumer %s did not terminate correctly", id); } } private class ConsumerMessageListener implements TopicMessageListener { @Override public void onMessage(TopicMessageEvent event) { Info info = (Info)event.getData(); log.info("Consumer %s: received message %s", id, event.getData()); received.add(info); if (received.size() == 10) { log.info("Consumer %s: received all messages before restart", id); // All messages received try { barriers[1].await(); } catch (Exception e) { Thread.currentThread().interrupt(); } } else if (received.size() == 20) { log.info("Consumer %s: received all messages after restart", id); // All messages received try { barriers[2].await(); } catch (Exception e) { Thread.currentThread().interrupt(); } consumer.unsubscribe(new ResultIssuesResponseListener() { @Override public void onResult(ResultEvent event) { log.info("Consumer %s: unsubscribed %s", id, event.getResult()); waitToStop.countDown(); } @Override public void onIssue(IssueEvent event) { log.error("Consumer %s: unsubscription failed %s", id, event.toString()); } }); } } } } }