com.wibidata.maven.plugins.hbase.MiniHBaseCluster.java Source code

Java tutorial

Introduction

Here is the source code for com.wibidata.maven.plugins.hbase.MiniHBaseCluster.java

Source

/**
 * Licensed to WibiData, Inc. under one or more contributor license
 * agreements.  See the NOTICE file distributed with this work for
 * additional information regarding copyright ownership.  WibiData, Inc.
 * 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 com.wibidata.maven.plugins.hbase;

import java.io.IOException;
import java.net.ServerSocket;
import java.util.Random;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseTestingUtility;
import org.apache.hadoop.hbase.HConstants;
import org.apache.maven.plugin.logging.Log;

/**
 * A in-process mini HBase cluster that may be started and stopped.
 */
public class MiniHBaseCluster extends MavenLogged {

    /** An HBase testing utility for starting/stopping the cluster. */
    private final HBaseTestingUtility mTestUtil;

    /** Whether a mini MapReduce cluster should also be run. */
    private final boolean mIsMapReduceEnabled;

    /** Whether the cluster is running. */
    private boolean mIsRunning;

    /**
     * Creates a new <code>MiniHBaseCluster</code> instance.
     *
     * @param log The maven log.
     * @param enableMapReduce Whether to also use a mini MapReduce cluster.
     * @param conf Hadoop configuration for the cluster.
     */
    public MiniHBaseCluster(Log log, boolean enableMapReduce, Configuration conf) {
        this(log, enableMapReduce, new HBaseTestingUtility(conf));
    }

    /**
     * Creates a new <code>MiniHBaseCluster</code> instance.
     *
     * @param log The maven log.
     * @param enableMapReduce Whether to also use a mini MapReduce cluster.
     * @param hbaseTestUtil An HBase testing utility that can start and stop mini clusters.
     */
    public MiniHBaseCluster(Log log, boolean enableMapReduce, HBaseTestingUtility hbaseTestUtil) {
        super(log);
        mTestUtil = configure(hbaseTestUtil);
        mIsMapReduceEnabled = enableMapReduce;
        mIsRunning = false;
    }

    /**
     * Provides access to the HBase cluster configuration.
     *
     * @return The cluster configuration.
     */
    public Configuration getConfiguration() {
        return mTestUtil.getConfiguration();
    }

    /**
     * Determine whether the cluster is running.
     *
     * @return Whether the cluster is running.
     */
    public boolean isRunning() {
        return mIsRunning;
    }

    /**
     * Starts the cluster.  Blocks until ready.
     *
     * @throws Exception If there is an error.
     */
    public void startup() throws Exception {
        if (isRunning()) {
            throw new RuntimeException("Cluster already running.");
        }
        mTestUtil.startMiniCluster();
        if (mIsMapReduceEnabled) {
            getLog().info("Starting MapReduce cluster...");

            // Work around a bug in HBaseTestingUtility that requires this conf var to be set.
            getConfiguration().set("hadoop.log.dir", getConfiguration().get("hadoop.tmp.dir"));

            // Start a mini MapReduce cluster with one server.
            mTestUtil.startMiniMapReduceCluster(1);

            // Set the mapred.working.dir so stuff like partition files get written somewhere reasonable.
            getConfiguration().set("mapred.working.dir", mTestUtil.getTestDir().toString());

            getLog().info("MapReduce cluster started.");
        }
        mIsRunning = true;
    }

    /**
     * Stops the cluster.  Blocks until shut down.
     *
     * @throws IOException If there is an error.
     */
    public void shutdown() throws IOException {
        if (!mIsRunning) {
            getLog().error("Attempting to shut down a cluster, but one was never started in this process.");
            return;
        }
        if (mIsMapReduceEnabled) {
            getLog().info("Shutting down MapReduce cluster...");
            mTestUtil.shutdownMiniMapReduceCluster();
            getLog().info("MapReduce cluster shut down.");
        }
        mTestUtil.shutdownMiniCluster();
    }

    /**
     * Configures an HBase testing utility.
     *
     * @param testUtil The test utility to configure.
     * @return The configured utility.
     */
    private static HBaseTestingUtility configure(HBaseTestingUtility testUtil) {
        // If HBase servers are running locally, the utility will use
        // the "normal" ports. We override *all* ports first, so that
        // we ensure that this can start without a problem.
        Configuration conf = testUtil.getConfiguration();

        int offset = new Random(System.currentTimeMillis()).nextInt(1500) + 500;

        // Move the master to a hopefully unused port.
        conf.setInt(HConstants.MASTER_PORT, findOpenPort(HConstants.DEFAULT_MASTER_PORT + offset));
        // Disable the master's web UI.
        conf.setInt("hbase.master.info.port", -1);

        // Move the regionserver to a hopefully unused port.
        conf.setInt(HConstants.REGIONSERVER_PORT, findOpenPort(HConstants.DEFAULT_REGIONSERVER_PORT + offset));
        // Disable the regionserver's web UI.
        conf.setInt("hbase.regionserver.info.port", -1);

        // Increase max zookeeper client connections.
        conf.setInt("hbase.zookeeper.property.maxClientCnxns", 80);

        // TODO(gwu): Increasing the port numbers by a constant is not sufficient for multiple
        // executions of this plugin on the same machine.  Allow this to be specified as a
        // maven plugin parameter.

        return testUtil;
    }

    /**
     * Find an available port.
     *
     * @param startPort the starting port to check.
     * @return an open port number.
     * @throws IllegalArgumentException if it can't find an open port.
     */
    public static int findOpenPort(int startPort) {
        if (startPort < 1024 || startPort > 65534) {
            throw new IllegalArgumentException("Invalid start port: " + startPort);
        }

        for (int port = startPort; port < 65534; port++) {
            ServerSocket ss = null;
            try {
                ss = new ServerSocket(port);
                ss.setReuseAddress(true);
                return port;
            } catch (IOException ioe) {
                // This port isn't open. Loop around.
            } finally {
                if (ss != null) {
                    try {
                        // TODO(aaron): Techincally, this causes a race condition. Another instance of
                        // findOpenPort() could determine that this port is available between now and
                        // when the client of this method calls this function.
                        ss.close();
                    } catch (IOException ioe) {
                        // Shouldn't happen.
                    }
                }
            }
        }

        throw new IllegalArgumentException("No port available starting at " + startPort);
    }
}