/** * 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.quorum; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.FilenameFilter; import java.io.IOException; import java.util.Properties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.common.PathUtils; import org.apache.zookeeper.server.admin.AdminServer.AdminServerException; import org.apache.zookeeper.server.admin.JettyAdminServer; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.QuorumBase; /** * Has some common functionality for tests that work with QuorumPeers. Override * process(WatchedEvent) to implement the Watcher interface */ public class QuorumPeerTestBase extends ZKTestCase implements Watcher { protected static final Logger LOG = LoggerFactory .getLogger(QuorumPeerTestBase.class); public static final int TIMEOUT = 3000; public void process(WatchedEvent event) { // ignore for this test } public static class TestQPMain extends QuorumPeerMain { public void shutdown() { // ensure it closes - in particular wait for thread to exit if (quorumPeer != null) { QuorumBase.shutdown(quorumPeer); } } } public static class MainThread implements Runnable { final File confFile; final File tmpDir; public static final int UNSET_STATIC_CLIENTPORT = -1; // standalone mode doens't need myid public static final int UNSET_MYID = -1; volatile TestQPMain main; public MainThread(int myid, String quorumCfgSection) throws IOException { this(myid, quorumCfgSection, true); } public MainThread(int myid, String quorumCfgSection, Integer secureClientPort, boolean writeDynamicConfigFile) throws IOException { this(myid, UNSET_STATIC_CLIENTPORT, JettyAdminServer.DEFAULT_PORT, secureClientPort, quorumCfgSection, null, null, writeDynamicConfigFile, null); } public MainThread(int myid, String quorumCfgSection, boolean writeDynamicConfigFile) throws IOException { this(myid, UNSET_STATIC_CLIENTPORT, quorumCfgSection, writeDynamicConfigFile); } public MainThread(int myid, int clientPort, String quorumCfgSection) throws IOException { this(myid, clientPort, JettyAdminServer.DEFAULT_PORT, quorumCfgSection, null, null, true); } public MainThread(int myid, int clientPort, String quorumCfgSection, boolean writeDynamicConfigFile) throws IOException { this(myid, clientPort, JettyAdminServer.DEFAULT_PORT, quorumCfgSection, null, null, writeDynamicConfigFile); } public MainThread(int myid, int clientPort, String quorumCfgSection, String peerType, boolean writeDynamicConfigFile) throws IOException { this(myid, clientPort, JettyAdminServer.DEFAULT_PORT, quorumCfgSection, null, peerType, writeDynamicConfigFile); } public MainThread(int myid, int clientPort, String quorumCfgSection, boolean writeDynamicConfigFile, String version) throws IOException { this(myid, clientPort, JettyAdminServer.DEFAULT_PORT, quorumCfgSection, null, null, writeDynamicConfigFile, version); } public MainThread(int myid, int clientPort, String quorumCfgSection, String configs) throws IOException { this(myid, clientPort, JettyAdminServer.DEFAULT_PORT, quorumCfgSection, configs, null, true); } public MainThread(int myid, int clientPort, int adminServerPort, String quorumCfgSection, String configs) throws IOException { this(myid, clientPort, adminServerPort, quorumCfgSection, configs, null, true); } public MainThread(int myid, int clientPort, int adminServerPort, String quorumCfgSection, String configs, String peerType, boolean writeDynamicConfigFile) throws IOException { this(myid, clientPort, adminServerPort, quorumCfgSection, configs, peerType, writeDynamicConfigFile, null); } public MainThread(int myid, int clientPort, int adminServerPort, String quorumCfgSection, String configs, String peerType, boolean writeDynamicConfigFile, String version) throws IOException { this(myid, clientPort, adminServerPort, null, quorumCfgSection, configs, peerType, writeDynamicConfigFile, version); } public MainThread(int myid, int clientPort, int adminServerPort, Integer secureClientPort, String quorumCfgSection, String configs, String peerType, boolean writeDynamicConfigFile, String version) throws IOException { tmpDir = ClientBase.createTmpDir(); LOG.info("id = " + myid + " tmpDir = " + tmpDir + " clientPort = " + clientPort + " adminServerPort = " + adminServerPort); File dataDir = new File(tmpDir, "data"); if (!dataDir.mkdir()) { throw new IOException("Unable to mkdir " + dataDir); } confFile = new File(tmpDir, "zoo.cfg"); FileWriter fwriter = new FileWriter(confFile); fwriter.write("tickTime=4000\n"); fwriter.write("initLimit=10\n"); fwriter.write("syncLimit=5\n"); if(configs != null){ fwriter.write(configs); } // Convert windows path to UNIX to avoid problems with "\" String dir = PathUtils.normalizeFileSystemPath(dataDir.toString()); fwriter.write("dataDir=" + dir + "\n"); fwriter.write("admin.serverPort=" + adminServerPort + "\n"); // For backward compatibility test, some tests create dynamic configuration // without setting client port. // This could happen both in static file or dynamic file. if (clientPort != UNSET_STATIC_CLIENTPORT) { fwriter.write("clientPort=" + clientPort + "\n"); } if (secureClientPort != null) { fwriter.write("secureClientPort=" + secureClientPort + "\n"); } if (peerType != null) { fwriter.write("peerType=" + peerType + "\n"); } if (writeDynamicConfigFile) { String dynamicConfigFilename = createDynamicFile(quorumCfgSection, version); fwriter.write("dynamicConfigFile=" + dynamicConfigFilename + "\n"); } else { fwriter.write(quorumCfgSection); } fwriter.flush(); fwriter.close(); File myidFile = new File(dataDir, "myid"); fwriter = new FileWriter(myidFile); fwriter.write(Integer.toString(myid)); fwriter.flush(); fwriter.close(); ClientBase.createInitializeFile(dataDir); } private String createDynamicFile(String quorumCfgSection, String version) throws IOException { String filename = "zoo.cfg.dynamic"; if( version != null ){ filename = filename + "." + version; } File dynamicConfigFile = new File(tmpDir, filename); String dynamicConfigFilename = PathUtils.normalizeFileSystemPath(dynamicConfigFile.toString()); FileWriter fDynamicConfigWriter = new FileWriter(dynamicConfigFile); fDynamicConfigWriter.write(quorumCfgSection); fDynamicConfigWriter.flush(); fDynamicConfigWriter.close(); return dynamicConfigFilename; } public File[] getDynamicFiles() { return getFilesWithPrefix("zoo.cfg.dynamic"); } public File[] getFilesWithPrefix(final String prefix) { return tmpDir.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.startsWith(prefix); }}); } public File getFileByName(String filename) { File f = new File(tmpDir.getPath(), filename); return f.isFile() ? f : null; } public void writeTempDynamicConfigFile(String nextQuorumCfgSection, String version) throws IOException { File nextDynamicConfigFile = new File(tmpDir, "zoo.cfg" + QuorumPeerConfig.nextDynamicConfigFileSuffix); FileWriter fwriter = new FileWriter(nextDynamicConfigFile); fwriter.write(nextQuorumCfgSection + "\n" + "version=" + version); fwriter.flush(); fwriter.close(); } Thread currentThread; synchronized public void start() { main = getTestQPMain(); currentThread = new Thread(this); currentThread.start(); } public TestQPMain getTestQPMain() { return new TestQPMain(); } public void run() { String args[] = new String[1]; args[0] = confFile.toString(); try { main.initializeAndRun(args); } catch (Exception e) { // test will still fail even though we just log/ignore LOG.error("unexpected exception in run", e); } finally { currentThread = null; } } public void shutdown() throws InterruptedException { Thread t = currentThread; if (t != null && t.isAlive()) { main.shutdown(); t.join(500); } } public void join(long timeout) throws InterruptedException { Thread t = currentThread; if (t != null) { t.join(timeout); } } public boolean isAlive() { Thread t = currentThread; return t != null && t.isAlive(); } public void reinitialize() throws IOException { File dataDir = main.quorumPeer.getTxnFactory().getDataDir(); ClientBase.recursiveDelete(dataDir); ClientBase.createInitializeFile(dataDir.getParentFile()); } public boolean isQuorumPeerRunning() { return main.quorumPeer != null; } public String getPropFromStaticFile(String key) throws IOException { Properties props = new Properties(); props.load(new FileReader(confFile)); return props.getProperty(key, ""); } public QuorumPeer getQuorumPeer() { return main.quorumPeer; } } }