blob: 9435711b357038848e35d10656461180b61fdcec [file] [log] [blame]
/**
* 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);
}
}
}