org.apache.hadoop.registry.server.services.MicroZookeeperService.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.registry.server.services.MicroZookeeperService.java

Source

/*
 * 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.hadoop.registry.server.services;

import com.google.common.base.Preconditions;
import org.apache.commons.lang.StringUtils;
import org.apache.curator.ensemble.fixed.FixedEnsembleProvider;
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileUtil;
import org.apache.hadoop.service.AbstractService;
import org.apache.hadoop.registry.client.api.RegistryConstants;
import org.apache.hadoop.registry.client.impl.zk.BindingInformation;
import org.apache.hadoop.registry.client.impl.zk.RegistryBindingSource;
import org.apache.hadoop.registry.client.impl.zk.RegistryInternalConstants;
import org.apache.hadoop.registry.client.impl.zk.RegistrySecurity;
import org.apache.hadoop.registry.client.impl.zk.ZookeeperConfigOptions;
import org.apache.zookeeper.server.ServerCnxnFactory;
import org.apache.zookeeper.server.ZooKeeperServer;
import org.apache.zookeeper.server.persistence.FileTxnSnapLog;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;

/**
 * This is a small, localhost Zookeeper service instance that is contained
 * in a YARN service...it's been derived from Apache Twill.
 * <p>
 * It implements {@link RegistryBindingSource} and provides binding information,
 * <i>once started</i>. Until {@link #start()} is called, the hostname and
 * port may be undefined. Accordingly, the service raises an exception in this
 * condition.
 * <p>
 * If you wish to chain together a registry service with this one under
 * the same {@code CompositeService}, this service must be added
 * as a child first.
 * <p>
 * It also sets the configuration parameter
 * {@link RegistryConstants#KEY_REGISTRY_ZK_QUORUM}
 * to its connection string. Any code with access to the service configuration
 * can view it.
 */
@InterfaceStability.Evolving
public class MicroZookeeperService extends AbstractService
        implements RegistryBindingSource, RegistryConstants, ZookeeperConfigOptions, MicroZookeeperServiceKeys {

    private static final Logger LOG = LoggerFactory.getLogger(MicroZookeeperService.class);

    private File instanceDir;
    private File dataDir;
    private int tickTime;
    private int port;
    private String host;
    private boolean secureServer;

    private ServerCnxnFactory factory;
    private BindingInformation binding;
    private File confDir;
    private StringBuilder diagnostics = new StringBuilder();

    /**
     * Create an instance
     * @param name service name
     */
    public MicroZookeeperService(String name) {
        super(name);
    }

    /**
     * Get the connection string.
     * @return the string
     * @throws IllegalStateException if the connection is not yet valid
     */
    public String getConnectionString() {
        Preconditions.checkState(factory != null, "service not started");
        InetSocketAddress addr = factory.getLocalAddress();
        return String.format("%s:%d", addr.getHostName(), addr.getPort());
    }

    /**
     * Get the connection address
     * @return the connection as an address
     * @throws IllegalStateException if the connection is not yet valid
     */
    public InetSocketAddress getConnectionAddress() {
        Preconditions.checkState(factory != null, "service not started");
        return factory.getLocalAddress();
    }

    /**
     * Create an inet socket addr from the local host + port number
     * @param port port to use
     * @return a (hostname, port) pair
     * @throws UnknownHostException if the server cannot resolve the host
     */
    private InetSocketAddress getAddress(int port) throws UnknownHostException {
        return new InetSocketAddress(host, port < 0 ? 0 : port);
    }

    /**
     * Initialize the service, including choosing a path for the data
     * @param conf configuration
     * @throws Exception
     */
    @Override
    protected void serviceInit(Configuration conf) throws Exception {
        port = conf.getInt(KEY_ZKSERVICE_PORT, 0);
        tickTime = conf.getInt(KEY_ZKSERVICE_TICK_TIME, ZooKeeperServer.DEFAULT_TICK_TIME);
        String instancedirname = conf.getTrimmed(KEY_ZKSERVICE_DIR, "");
        host = conf.getTrimmed(KEY_ZKSERVICE_HOST, DEFAULT_ZKSERVICE_HOST);
        if (instancedirname.isEmpty()) {
            File testdir = new File(System.getProperty("test.dir", "target"));
            instanceDir = new File(testdir, "zookeeper" + getName());
        } else {
            instanceDir = new File(instancedirname);
            FileUtil.fullyDelete(instanceDir);
        }
        LOG.debug("Instance directory is {}", instanceDir);
        mkdirStrict(instanceDir);
        dataDir = new File(instanceDir, "data");
        confDir = new File(instanceDir, "conf");
        mkdirStrict(dataDir);
        mkdirStrict(confDir);
        super.serviceInit(conf);
    }

    /**
     * Create a directory, ignoring if the dir is already there,
     * and failing if a file or something else was at the end of that
     * path
     * @param dir dir to guarantee the existence of
     * @throws IOException IO problems, or path exists but is not a dir
     */
    private void mkdirStrict(File dir) throws IOException {
        if (!dir.mkdirs()) {
            if (!dir.isDirectory()) {
                throw new IOException("Failed to mkdir " + dir);
            }
        }
    }

    /**
     * Append a formatted string to the diagnostics.
     * <p>
     * A newline is appended afterwards.
     * @param text text including any format commands
     * @param args arguments for the forma operation.
     */
    protected void addDiagnostics(String text, Object... args) {
        diagnostics.append(String.format(text, args)).append('\n');
    }

    /**
     * Get the diagnostics info
     * @return the diagnostics string built up
     */
    public String getDiagnostics() {
        return diagnostics.toString();
    }

    /**
     * set up security. this must be done prior to creating
     * the ZK instance, as it sets up JAAS if that has not been done already.
     *
     * @return true if the cluster has security enabled.
     */
    public boolean setupSecurity() throws IOException {
        Configuration conf = getConfig();
        String jaasContext = conf.getTrimmed(KEY_REGISTRY_ZKSERVICE_JAAS_CONTEXT);
        secureServer = StringUtils.isNotEmpty(jaasContext);
        if (secureServer) {
            RegistrySecurity.validateContext(jaasContext);
            RegistrySecurity.bindZKToServerJAASContext(jaasContext);
            // policy on failed auth
            System.setProperty(PROP_ZK_ALLOW_FAILED_SASL_CLIENTS,
                    conf.get(KEY_ZKSERVICE_ALLOW_FAILED_SASL_CLIENTS, "true"));

            //needed so that you can use sasl: strings in the registry
            System.setProperty(RegistryInternalConstants.ZOOKEEPER_AUTH_PROVIDER + ".1",
                    RegistryInternalConstants.SASLAUTHENTICATION_PROVIDER);
            String serverContext = System.getProperty(PROP_ZK_SERVER_SASL_CONTEXT);
            addDiagnostics("Server JAAS context s = %s", serverContext);
            return true;
        } else {
            return false;
        }
    }

    /**
     * Startup: start ZK. It is only after this that
     * the binding information is valid.
     * @throws Exception
     */
    @Override
    protected void serviceStart() throws Exception {

        setupSecurity();

        ZooKeeperServer zkServer = new ZooKeeperServer();
        FileTxnSnapLog ftxn = new FileTxnSnapLog(dataDir, dataDir);
        zkServer.setTxnLogFactory(ftxn);
        zkServer.setTickTime(tickTime);

        LOG.info("Starting Local Zookeeper service");
        factory = ServerCnxnFactory.createFactory();
        factory.configure(getAddress(port), -1);
        factory.startup(zkServer);

        String connectString = getConnectionString();
        LOG.info("In memory ZK started at {}\n", connectString);

        if (LOG.isDebugEnabled()) {
            StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter(sw);
            zkServer.dumpConf(pw);
            pw.flush();
            LOG.debug(sw.toString());
        }
        binding = new BindingInformation();
        binding.ensembleProvider = new FixedEnsembleProvider(connectString);
        binding.description = getName() + " reachable at \"" + connectString + "\"";

        addDiagnostics(binding.description);
        // finally: set the binding information in the config
        getConfig().set(KEY_REGISTRY_ZK_QUORUM, connectString);
    }

    /**
     * When the service is stopped, it deletes the data directory
     * and its contents
     * @throws Exception
     */
    @Override
    protected void serviceStop() throws Exception {
        if (factory != null) {
            factory.shutdown();
            factory = null;
        }
        if (dataDir != null) {
            FileUtil.fullyDelete(dataDir);
        }
    }

    @Override
    public BindingInformation supplyBindingInformation() {
        Preconditions.checkNotNull(binding, "Service is not started: binding information undefined");
        return binding;
    }
}