| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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.apache.zookeeper.server; |
| |
| import static org.apache.zookeeper.client.FourLetterWordMain.send4LetterWord; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.concurrent.CountDownLatch; |
| import java.util.concurrent.TimeUnit; |
| |
| import org.apache.zookeeper.PortAssignment; |
| import org.apache.zookeeper.ZKTestCase; |
| import org.apache.zookeeper.ZooKeeper; |
| import org.apache.zookeeper.test.ClientBase; |
| import org.apache.zookeeper.test.ClientBase.CountdownWatcher; |
| import org.junit.After; |
| import org.junit.Assert; |
| import org.junit.Test; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * This class tests the startup behavior of ZooKeeper server. |
| */ |
| public class ZooKeeperServerStartupTest extends ZKTestCase { |
| private static final Logger LOG = LoggerFactory |
| .getLogger(ZooKeeperServerStartupTest.class); |
| private static int PORT = PortAssignment.unique(); |
| private static String HOST = "127.0.0.1"; |
| private static String HOSTPORT = HOST + ":" + PORT; |
| private static final String ZK_NOT_SERVING = "This ZooKeeper instance is not currently serving requests"; |
| |
| private ServerCnxnFactory servcnxnf; |
| private ZooKeeperServer zks; |
| private File tmpDir; |
| private CountDownLatch startupDelayLatch = new CountDownLatch(1); |
| |
| @After |
| public void teardown() throws Exception { |
| // count down to avoid infinite blocking call due to this latch, if |
| // any. |
| startupDelayLatch.countDown(); |
| |
| if (servcnxnf != null) { |
| servcnxnf.shutdown(); |
| } |
| if (zks != null) { |
| zks.shutdown(); |
| } |
| if (zks.getZKDatabase() != null) { |
| zks.getZKDatabase().close(); |
| } |
| if (tmpDir != null) { |
| ClientBase.recursiveDelete(tmpDir); |
| } |
| } |
| |
| /** |
| * Test case for |
| * {@link https://issues.apache.org/jira/browse/ZOOKEEPER-2383}. |
| */ |
| @Test(timeout = 30000) |
| public void testClientConnectionRequestDuringStartupWithNIOServerCnxn() |
| throws Exception { |
| tmpDir = ClientBase.createTmpDir(); |
| ClientBase.setupTestEnv(); |
| |
| startSimpleZKServer(startupDelayLatch); |
| SimpleZooKeeperServer simplezks = (SimpleZooKeeperServer) zks; |
| Assert.assertTrue( |
| "Failed to invoke zks#startup() method during server startup", |
| simplezks.waitForStartupInvocation(10)); |
| |
| CountdownWatcher watcher = new CountdownWatcher(); |
| ZooKeeper zkClient = new ZooKeeper(HOSTPORT, |
| ClientBase.CONNECTION_TIMEOUT, watcher); |
| |
| Assert.assertFalse( |
| "Since server is not fully started, zks#createSession() shouldn't be invoked", |
| simplezks.waitForSessionCreation(5)); |
| |
| LOG.info( |
| "Decrements the count of the latch, so that server will proceed with startup"); |
| startupDelayLatch.countDown(); |
| |
| Assert.assertTrue("waiting for server being up ", ClientBase |
| .waitForServerUp(HOSTPORT, ClientBase.CONNECTION_TIMEOUT)); |
| |
| Assert.assertTrue( |
| "Failed to invoke zks#createSession() method during client session creation", |
| simplezks.waitForSessionCreation(5)); |
| watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); |
| zkClient.close(); |
| } |
| |
| /** |
| * Test case for |
| * {@link https://issues.apache.org/jira/browse/ZOOKEEPER-2383}. |
| */ |
| @Test(timeout = 30000) |
| public void testClientConnectionRequestDuringStartupWithNettyServerCnxn() |
| throws Exception { |
| tmpDir = ClientBase.createTmpDir(); |
| ClientBase.setupTestEnv(); |
| |
| String originalServerCnxnFactory = System |
| .getProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY); |
| try { |
| System.setProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, |
| NettyServerCnxnFactory.class.getName()); |
| startSimpleZKServer(startupDelayLatch); |
| SimpleZooKeeperServer simplezks = (SimpleZooKeeperServer) zks; |
| Assert.assertTrue( |
| "Failed to invoke zks#startup() method during server startup", |
| simplezks.waitForStartupInvocation(10)); |
| |
| CountdownWatcher watcher = new CountdownWatcher(); |
| ZooKeeper zkClient = new ZooKeeper(HOSTPORT, |
| ClientBase.CONNECTION_TIMEOUT, watcher); |
| |
| Assert.assertFalse( |
| "Since server is not fully started, zks#createSession() shouldn't be invoked", |
| simplezks.waitForSessionCreation(5)); |
| |
| LOG.info( |
| "Decrements the count of the latch, so that server will proceed with startup"); |
| startupDelayLatch.countDown(); |
| |
| Assert.assertTrue("waiting for server being up ", ClientBase |
| .waitForServerUp(HOSTPORT, ClientBase.CONNECTION_TIMEOUT)); |
| |
| Assert.assertTrue( |
| "Failed to invoke zks#createSession() method during client session creation", |
| simplezks.waitForSessionCreation(5)); |
| watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); |
| zkClient.close(); |
| } finally { |
| // reset cnxn factory |
| if (originalServerCnxnFactory == null) { |
| System.clearProperty( |
| ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY); |
| } else { |
| System.setProperty( |
| ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, |
| originalServerCnxnFactory); |
| } |
| } |
| } |
| |
| /** |
| * Test case for |
| * {@link https://issues.apache.org/jira/browse/ZOOKEEPER-2383}. |
| */ |
| @Test(timeout = 30000) |
| public void testFourLetterWordsWithNIOServerCnxn() throws Exception { |
| startSimpleZKServer(startupDelayLatch); |
| verify("conf", ZK_NOT_SERVING); |
| verify("crst", ZK_NOT_SERVING); |
| verify("cons", ZK_NOT_SERVING); |
| verify("dump", ZK_NOT_SERVING); |
| verify("mntr", ZK_NOT_SERVING); |
| verify("stat", ZK_NOT_SERVING); |
| verify("srst", ZK_NOT_SERVING); |
| verify("wchs", ZK_NOT_SERVING); |
| verify("isro", "null"); |
| } |
| |
| /** |
| * Test case for |
| * {@link https://issues.apache.org/jira/browse/ZOOKEEPER-2383}. |
| */ |
| @Test(timeout = 30000) |
| public void testFourLetterWordsWithNettyServerCnxn() throws Exception { |
| String originalServerCnxnFactory = System |
| .getProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY); |
| try { |
| startSimpleZKServer(startupDelayLatch); |
| verify("conf", ZK_NOT_SERVING); |
| verify("crst", ZK_NOT_SERVING); |
| verify("cons", ZK_NOT_SERVING); |
| verify("dump", ZK_NOT_SERVING); |
| verify("mntr", ZK_NOT_SERVING); |
| verify("stat", ZK_NOT_SERVING); |
| verify("srst", ZK_NOT_SERVING); |
| verify("wchs", ZK_NOT_SERVING); |
| verify("isro", "null"); |
| } finally { |
| // reset cnxn factory |
| if (originalServerCnxnFactory == null) { |
| System.clearProperty( |
| ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY); |
| } else { |
| System.setProperty( |
| ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, |
| originalServerCnxnFactory); |
| } |
| } |
| } |
| |
| private void verify(String cmd, String expected) |
| throws IOException { |
| String resp = sendRequest(cmd); |
| LOG.info("cmd " + cmd + " expected " + expected + " got " + resp); |
| Assert.assertTrue("Unexpected response: " + resp, |
| resp.contains(expected)); |
| } |
| |
| private String sendRequest(String cmd) |
| throws IOException { |
| return send4LetterWord(HOST, PORT, cmd); |
| } |
| |
| private void startSimpleZKServer(CountDownLatch startupDelayLatch) |
| throws IOException { |
| zks = new SimpleZooKeeperServer(tmpDir, tmpDir, 3000, |
| startupDelayLatch); |
| SyncRequestProcessor.setSnapCount(100); |
| final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); |
| |
| servcnxnf = ServerCnxnFactory.createFactory(PORT, -1); |
| Thread startupThread = new Thread() { |
| public void run() { |
| try { |
| servcnxnf.startup(zks); |
| } catch (IOException e) { |
| LOG.error("Unexcepted exception during server startup", e); |
| // Ignoring exception. If there is an ioexception |
| // then one of the following assertion will fail |
| } catch (InterruptedException e) { |
| LOG.error("Unexcepted exception during server startup", e); |
| // Ignoring exception. If there is an interrupted exception |
| // then one of the following assertion will fail |
| } |
| }; |
| }; |
| LOG.info("Starting zk server {}", HOSTPORT); |
| startupThread.start(); |
| } |
| |
| private static class SimpleZooKeeperServer extends ZooKeeperServer { |
| private CountDownLatch startupDelayLatch; |
| private CountDownLatch startupInvokedLatch = new CountDownLatch(1); |
| private CountDownLatch createSessionInvokedLatch = new CountDownLatch( |
| 1); |
| |
| public SimpleZooKeeperServer(File snapDir, File logDir, int tickTime, |
| CountDownLatch startupDelayLatch) throws IOException { |
| super(snapDir, logDir, tickTime); |
| this.startupDelayLatch = startupDelayLatch; |
| } |
| |
| @Override |
| public synchronized void startup() { |
| try { |
| startupInvokedLatch.countDown(); |
| // Delaying the zk server startup so that |
| // ZooKeeperServer#sessionTracker reference won't be |
| // initialized. In the defect scenario, while processing the |
| // connection request zkServer needs sessionTracker reference, |
| // but this is not yet initialized and the server is still in |
| // the startup phase, resulting in NPE. |
| startupDelayLatch.await(); |
| } catch (InterruptedException e) { |
| Assert.fail( |
| "Unexpected InterruptedException while startinng up!"); |
| } |
| super.startup(); |
| } |
| |
| @Override |
| long createSession(ServerCnxn cnxn, byte[] passwd, int timeout) { |
| createSessionInvokedLatch.countDown(); |
| return super.createSession(cnxn, passwd, timeout); |
| } |
| |
| boolean waitForStartupInvocation(long timeout) |
| throws InterruptedException { |
| return startupInvokedLatch.await(timeout, TimeUnit.SECONDS); |
| } |
| |
| boolean waitForSessionCreation(long timeout) |
| throws InterruptedException { |
| return createSessionInvokedLatch.await(timeout, TimeUnit.SECONDS); |
| } |
| } |
| } |