phoenix-queryserver-it/src/it/java/org/apache/phoenix/end2end/QueryServerTestUtil.java
<filename>phoenix-queryserver-it/src/it/java/org/apache/phoenix/end2end/QueryServerTestUtil.java/*
 * 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.phoenix.end2end;

import static org.apache.hadoop.hbase.HConstants.HBASE_DIR;

import java.io.File;
import java.security.PrivilegedAction;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.HBaseTestingUtility;
import org.apache.hadoop.hbase.LocalHBaseCluster;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.phoenix.query.ConfigurationFactory;
import org.apache.phoenix.queryserver.server.QueryServer;
import org.apache.phoenix.util.InstanceResolver;
import org.apache.phoenix.util.ThinClientUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class QueryServerTestUtil {
    private static final Logger LOG = LoggerFactory.getLogger(QueryServerTestUtil.class);

    private final Configuration conf;
    private final HBaseTestingUtility util;
    private LocalHBaseCluster hbase;

    private final QueryServer pqs;
    private int port;
    private String url;

    private String principal;
    private File keytab;

    private ExecutorService executor;

    public QueryServerTestUtil(Configuration conf) {
        this.conf = Objects.requireNonNull(conf);
        this.util = new HBaseTestingUtility(conf);
        this.pqs = new QueryServer(new String[0], conf);
    }

    public QueryServerTestUtil(Configuration conf, String principal, File keytab) {
        this.conf = Objects.requireNonNull(conf);
        this.principal = principal;
        this.keytab = keytab;
        this.util = new HBaseTestingUtility(conf);
        this.pqs = new QueryServer(new String[0], conf);
    }

    public void startLocalHBaseCluster(Class testClass) throws Exception {
        startLocalHBaseCluster(testClass.getCanonicalName());
    }

    public void startLocalHBaseCluster(String uniqueName) throws Exception {
        LOG.debug("Starting local HBase cluster for '{}'", uniqueName);
        // Start ZK
        util.startMiniZKCluster();
        // Start HDFS
        util.startMiniDFSCluster(1);
        // Start HBase
        Path rootdir = util.getDataTestDirOnTestFS(uniqueName);
        // There is no setRootdir method that is available in all supported HBase versions.
        conf.set(HBASE_DIR, rootdir.toString());
        hbase = new LocalHBaseCluster(conf, 1);
        hbase.startup();
    }

    public void stopLocalHBaseCluster() throws Exception {
        LOG.debug("Stopping local HBase cluster");
        if (hbase != null) {
            hbase.shutdown();
            hbase.join();
        }
        if (util != null) {
            util.shutdownMiniDFSCluster();
            util.shutdownMiniZKCluster();
        }
    }

    public void startQueryServer() throws Exception {
        setupQueryServerConfiguration(conf);
        executor = Executors.newSingleThreadExecutor();
        if (principal != null && !principal.isEmpty() && null != keytab) {
            // Get the PQS ident for PQS to use
            final UserGroupInformation ugi = UserGroupInformation
                    .loginUserFromKeytabAndReturnUGI(principal, keytab.getAbsolutePath());
            // Launch PQS, doing in the Kerberos login instead of letting PQS do it itself (which would
            // break the HBase/HDFS logins also running in the same test case).
            executor.submit(new Runnable() {
                @Override
                public void run() {
                    ugi.doAs(new PrivilegedAction<Void>() {
                        @Override
                        public Void run() {
                            pqs.run();
                            return null;
                        }
                    });
                }
            });
        } else {
            // Launch PQS without a login
            executor.submit(new Runnable() {
                @Override
                public void run() {
                    pqs.run();
                }
            });
        }
        pqs.awaitRunning();
        port = pqs.getPort();
        url = ThinClientUtil.getConnectionUrl("localhost", port);
    }

    public void stopQueryServer() throws Exception {
        if (pqs != null) {
            pqs.stop();
        }
        if (executor != null) {
            executor.shutdown();
            if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
                LOG.info("PQS didn't exit in 5 seconds, proceeding anyways.");
            }
        }
    }

    public static void setupQueryServerConfiguration(final Configuration conf) {
        // Make sure the ConnectionInfo doesn't try to pull a default Configuration
        InstanceResolver.getSingleton(ConfigurationFactory.class, new ConfigurationFactory() {
            @Override
            public Configuration getConfiguration() {
                return conf;
            }
            @Override
            public Configuration getConfiguration(Configuration confToClone) {
                Configuration copy = new Configuration(conf);
                copy.addResource(confToClone);
                return copy;
            }
        });
    }

    public int getPort() {
        return port;
    }

    public String getUrl() {
        return url;
    }

    /**
     * Returns the query server URL with the specified URL params
     * @param params URL params
     * @return URL with params
     */
    public String getUrl(Map<String, String> params) {
        if (params == null || params.size() == 0) {
            return url;
        }
        StringBuilder urlParams = new StringBuilder();
        for (Map.Entry<String, String> param : params.entrySet()) {
            urlParams.append(";").append(param.getKey()).append("=").append(param.getValue());
        }
        return url + urlParams;
    }
}
