001/**
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
004 *
005 * This program and the accompanying materials are dual-licensed under
006 * either the terms of the Eclipse Public License v1.0 as published by
007 * the Eclipse Foundation
008 *
009 *   or (per the licensee's choosing)
010 *
011 * under the terms of the GNU Lesser General Public License version 2.1
012 * as published by the Free Software Foundation.
013 */
014package ch.qos.logback.core.net.server;
015
016import static org.junit.Assert.assertFalse;
017import static org.junit.Assert.assertNotNull;
018import static org.junit.Assert.assertSame;
019import static org.junit.Assert.assertTrue;
020
021import java.util.concurrent.Executor;
022import java.util.concurrent.ExecutorService;
023import java.util.concurrent.Executors;
024import java.util.concurrent.TimeUnit;
025import java.util.concurrent.locks.Condition;
026import java.util.concurrent.locks.Lock;
027import java.util.concurrent.locks.ReentrantLock;
028
029import org.junit.After;
030import org.junit.Before;
031import org.junit.Test;
032
033import ch.qos.logback.core.net.mock.MockContext;
034import ch.qos.logback.core.net.server.test.MockServerListener;
035
036public class ConcurrentServerRunnerTest {
037
038    private static final int DELAY = 10000;
039    private static final int SHORT_DELAY = 10;
040
041    private MockContext context = new MockContext();
042    private MockServerListener<MockClient> listener = new MockServerListener<MockClient>();
043
044    private ExecutorService executor = Executors.newCachedThreadPool();
045    private InstrumentedConcurrentServerRunner runner = new InstrumentedConcurrentServerRunner(listener, executor);
046
047    @Before
048    public void setUp() throws Exception {
049        runner.setContext(context);
050    }
051
052    @After
053    public void tearDown() throws Exception {
054        executor.shutdownNow();
055        assertTrue(executor.awaitTermination(DELAY, TimeUnit.MILLISECONDS));
056    }
057
058    @Test
059    public void testStartStop() throws Exception {
060        assertFalse(runner.isRunning());
061        executor.execute(runner);
062        assertTrue(runner.awaitRunState(true, DELAY));
063        int retries = DELAY / SHORT_DELAY;
064        synchronized (listener) {
065            while (retries-- > 0 && listener.getWaiter() == null) {
066                listener.wait(SHORT_DELAY);
067            }
068        }
069        assertNotNull(listener.getWaiter());
070        runner.stop();
071        assertTrue(listener.isClosed());
072        assertFalse(runner.awaitRunState(false, DELAY));
073    }
074
075    @Test
076    public void testRunOneClient() throws Exception {
077        executor.execute(runner);
078        MockClient client = new MockClient();
079        listener.addClient(client);
080        int retries = DELAY / SHORT_DELAY;
081        synchronized (client) {
082            while (retries-- > 0 && !client.isRunning()) {
083                client.wait(SHORT_DELAY);
084            }
085        }
086        assertTrue(runner.awaitRunState(true, DELAY));
087        client.close();
088        runner.stop();
089    }
090
091    @Test
092    public void testRunManyClients() throws Exception {
093        executor.execute(runner);
094        int count = 10;
095        while (count-- > 0) {
096            MockClient client = new MockClient();
097            listener.addClient(client);
098            int retries = DELAY / SHORT_DELAY;
099            synchronized (client) {
100                while (retries-- > 0 && !client.isRunning()) {
101                    client.wait(SHORT_DELAY);
102                }
103            }
104            assertTrue(runner.awaitRunState(true, DELAY));
105        }
106        runner.stop();
107    }
108
109    @Test
110    public void testRunClientAndVisit() throws Exception {
111        executor.execute(runner);
112        MockClient client = new MockClient();
113        listener.addClient(client);
114        int retries = DELAY / SHORT_DELAY;
115        synchronized (client) {
116            while (retries-- > 0 && !client.isRunning()) {
117                client.wait(SHORT_DELAY);
118            }
119        }
120        assertTrue(runner.awaitRunState(true, DELAY));
121        MockClientVisitor visitor = new MockClientVisitor();
122        runner.accept(visitor);
123        assertSame(client, visitor.getLastVisited());
124        runner.stop();
125    }
126
127    static class InstrumentedConcurrentServerRunner extends ConcurrentServerRunner<MockClient> {
128
129        private final Lock lock = new ReentrantLock();
130        private final Condition runningCondition = lock.newCondition();
131
132        public InstrumentedConcurrentServerRunner(ServerListener<MockClient> listener, Executor executor) {
133            super(listener, executor);
134        }
135
136        @Override
137        protected boolean configureClient(MockClient client) {
138            return true;
139        }
140
141        @Override
142        protected void setRunning(boolean running) {
143            lock.lock();
144            try {
145                super.setRunning(running);
146                runningCondition.signalAll();
147            } finally {
148                lock.unlock();
149            }
150        }
151
152        public boolean awaitRunState(boolean state, long delay) throws InterruptedException {
153            lock.lock();
154            try {
155                while (isRunning() != state) {
156                    runningCondition.await(delay, TimeUnit.MILLISECONDS);
157                }
158                return isRunning();
159            } finally {
160                lock.unlock();
161            }
162        }
163    }
164
165}