Java tutorial
/** * 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.s4.fixtures; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.Socket; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.CountDownLatch; import junit.framework.Assert; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.server.NIOServerCnxn; import org.apache.zookeeper.server.ZooKeeperServer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.io.Files; /** * Contains static methods that can be used in tests for things such as: - files utilities: strings <-> files * conversion, directory recursive delete etc... - starting local instances for zookeeper and bookkeeper - distributed * latches through zookeeper - etc... * */ public class CommTestUtils { private static final Logger logger = LoggerFactory.getLogger(CommTestUtils.class); public static final int ZK_PORT = 2181; public static final String ZK_STRING = "localhost:" + ZK_PORT; public static File DEFAULT_TEST_OUTPUT_DIR = new File( System.getProperty("java.io.tmpdir") + File.separator + "tmp"); public static File DEFAULT_STORAGE_DIR = new File( DEFAULT_TEST_OUTPUT_DIR.getAbsolutePath() + File.separator + "storage"); static { logger.info("Storage dir: " + DEFAULT_STORAGE_DIR); } public final static String MESSAGE = "message@" + System.currentTimeMillis(); public final static CountDownLatch SIGNAL_MESSAGE_RECEIVED = new CountDownLatch(1); protected static Process forkProcess(String mainClass, int debugPort, String... args) throws IOException, InterruptedException { List<String> cmdList = new ArrayList<String>(); cmdList.add("java"); cmdList.add("-cp"); cmdList.add(System.getProperty("java.class.path")); if (debugPort != -1) { cmdList.add("-Xdebug"); cmdList.add("-Xnoagent"); cmdList.add("-Xrunjdwp:transport=dt_socket,address=" + debugPort + ",server=y,suspend=n"); } cmdList.add(mainClass); for (String arg : args) { cmdList.add(arg); } System.out.println(Arrays.toString(cmdList.toArray(new String[] {})).replace(",", "")); ProcessBuilder pb = new ProcessBuilder(cmdList); pb.directory(new File(System.getProperty("user.dir"))); pb.redirectErrorStream(); final Process process = pb.start(); // TODO some synchro with s4 platform ready state Thread.sleep(2000); try { int exitValue = process.exitValue(); Assert.fail("forked process failed to start correctly. Exit code is [" + exitValue + "]"); } catch (IllegalThreadStateException ignored) { } new Thread(new Runnable() { @Override public void run() { BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream())); String line; try { line = br.readLine(); while (line != null) { System.out.println(line); line = br.readLine(); } } catch (IOException e) { throw new RuntimeException(e); } } }).start(); return process; } public static void killS4App(Process forkedApp) throws IOException, InterruptedException { if (forkedApp != null) { forkedApp.destroy(); } } public static void writeStringToFile(String s, File f) throws IOException { Files.write(s, f, Charset.defaultCharset()); } public static String readFile(File f) throws IOException { return Files.toString(f, Charset.defaultCharset()); } public static NIOServerCnxn.Factory startZookeeperServer() throws IOException, InterruptedException, KeeperException { final File zkDataDir = new File(System.getProperty("java.io.tmpdir") + File.separator + "tmp" + File.separator + "zookeeper" + File.separator + "data"); if (zkDataDir.exists()) { CommTestUtils.deleteDirectoryContents(zkDataDir); } else { zkDataDir.mkdirs(); } ZooKeeperServer zks = new ZooKeeperServer(zkDataDir, zkDataDir, 3000); NIOServerCnxn.Factory nioZookeeperConnectionFactory = new NIOServerCnxn.Factory( new InetSocketAddress(ZK_PORT)); nioZookeeperConnectionFactory.startup(zks); Assert.assertTrue("waiting for server being up", waitForServerUp("localhost", ZK_PORT, 4000)); return nioZookeeperConnectionFactory; } public static void stopZookeeperServer(NIOServerCnxn.Factory f) throws IOException, InterruptedException { if (f != null) { f.shutdown(); Assert.assertTrue("waiting for server down", waitForServerDown("localhost", ZK_PORT, 3000)); } } public static void deleteDirectoryContents(File dir) { File[] files = dir.listFiles(); for (File file : files) { if (file.isDirectory()) { deleteDirectoryContents(file); } if (!file.delete()) { throw new RuntimeException("could not delete : " + file); } } } public static String readFileAsString(File f) throws IOException { return Files.toString(f, Charset.defaultCharset()); } public static byte[] readFileAsByteArray(File file) throws Exception { return Files.toByteArray(file); } public static ZooKeeper createZkClient() throws IOException { final ZooKeeper zk = new ZooKeeper("localhost:" + ZK_PORT, 4000, new Watcher() { @Override public void process(WatchedEvent event) { } }); return zk; } public static void watchAndSignalCreation(String path, final CountDownLatch latch, final ZooKeeper zk) throws KeeperException, InterruptedException { // by default delete existing nodes with same path watchAndSignalCreation(path, latch, zk, false); } public static void watchAndSignalCreation(String path, final CountDownLatch latch, final ZooKeeper zk, boolean deleteIfExists) throws KeeperException, InterruptedException { if (zk.exists(path, false) != null) { if (deleteIfExists) { zk.delete(path, -1); } else { latch.countDown(); } } zk.exists(path, new Watcher() { @Override public void process(WatchedEvent event) { if (EventType.NodeCreated.equals(event.getType())) { latch.countDown(); } } }); } public static void watchAndSignalChangedChildren(String path, final CountDownLatch latch, final ZooKeeper zk) throws KeeperException, InterruptedException { zk.getChildren(path, new Watcher() { @Override public void process(WatchedEvent event) { if (EventType.NodeChildrenChanged.equals(event.getType())) { latch.countDown(); } } }); } // from zookeeper's codebase public static boolean waitForServerUp(String host, int port, long timeout) { long start = System.currentTimeMillis(); while (true) { try { // if there are multiple hostports, just take the first one String result = send4LetterWord(host, port, "stat"); if (result.startsWith("Zookeeper version:")) { return true; } } catch (IOException ignored) { // ignore as this is expected } if (System.currentTimeMillis() > start + timeout) { break; } try { Thread.sleep(250); } catch (InterruptedException e) { Thread.currentThread().interrupt(); // ignore } } return false; } // from zookeeper's codebase public static String send4LetterWord(String host, int port, String cmd) throws IOException { Socket sock = new Socket(host, port); BufferedReader reader = null; try { OutputStream outstream = sock.getOutputStream(); outstream.write(cmd.getBytes()); outstream.flush(); // this replicates NC - close the output stream before reading sock.shutdownOutput(); reader = new BufferedReader(new InputStreamReader(sock.getInputStream())); StringBuilder sb = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { sb.append(line + "\n"); } return sb.toString(); } finally { sock.close(); if (reader != null) { reader.close(); } } } // from zookeeper's codebase public static boolean waitForServerDown(String host, int port, long timeout) { long start = System.currentTimeMillis(); while (true) { try { send4LetterWord(host, port, "stat"); } catch (IOException e) { return true; } if (System.currentTimeMillis() > start + timeout) { break; } try { Thread.sleep(250); } catch (InterruptedException e) { Thread.currentThread().interrupt(); // ignore } } return false; } public static void cleanupTmpDirs() { if (CommTestUtils.DEFAULT_TEST_OUTPUT_DIR.exists()) { deleteDirectoryContents(CommTestUtils.DEFAULT_TEST_OUTPUT_DIR); } CommTestUtils.DEFAULT_STORAGE_DIR.mkdirs(); } /** * gradle and eclipse have different directories for output files This is justified here * http://gradle.1045684.n5.nabble.com/Changing-default-IDE-output-directories-td3335478.html#a3337433 * * A consequence is that for tests to reference compiled files, we need to resolve the corresponding directory at * runtime. * * This is what this method does * * @return directory containing the compiled test classes for this project and execution environment. */ public static File findDirForCompiledTestClasses() { String userDir = System.getProperty("user.dir"); String classpath = System.getProperty("java.class.path"); System.out.println(userDir); System.out.println(classpath); if (classpath.contains(userDir + "/bin")) { // eclipse classpath return new File(userDir + "/bin"); } else if (classpath.contains(userDir + "/build/classes/test")) { // gradle classpath return new File(userDir + "/build/classes/test"); } else { // TODO other IDEs throw new RuntimeException("Cannot find path for compiled test classes"); } } }