org.apache.solr.hadoop.ZooKeeperInspector.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.solr.hadoop.ZooKeeperInspector.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.solr.hadoop;

import java.io.File;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import com.google.common.io.Files;
import org.apache.commons.io.FileUtils;
import org.apache.solr.cloud.ZkController;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.cloud.Aliases;
import org.apache.solr.common.cloud.ClusterState;
import org.apache.solr.common.cloud.DocCollection;
import org.apache.solr.common.cloud.Replica;
import org.apache.solr.common.cloud.Slice;
import org.apache.solr.common.cloud.SolrZkClient;
import org.apache.solr.common.cloud.ZkConfigManager;
import org.apache.solr.common.cloud.ZkCoreNodeProps;
import org.apache.solr.common.cloud.ZkNodeProps;
import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.solr.common.util.StrUtils;
import org.apache.zookeeper.KeeperException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Extracts SolrCloud information from ZooKeeper.
 */
final class ZooKeeperInspector {

    private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

    public List<List<String>> extractShardUrls(String zkHost, String collection) {

        DocCollection docCollection = extractDocCollection(zkHost, collection);
        List<Slice> slices = getSortedSlices(docCollection.getSlices());
        List<List<String>> solrUrls = new ArrayList<>(slices.size());
        for (Slice slice : slices) {
            if (slice.getLeader() == null) {
                throw new IllegalArgumentException("Cannot find SolrCloud slice leader. "
                        + "It looks like not all of your shards are registered in ZooKeeper yet");
            }
            Collection<Replica> replicas = slice.getReplicas();
            List<String> urls = new ArrayList<>(replicas.size());
            for (Replica replica : replicas) {
                ZkCoreNodeProps props = new ZkCoreNodeProps(replica);
                urls.add(props.getCoreUrl());
            }
            solrUrls.add(urls);
        }
        return solrUrls;
    }

    public DocCollection extractDocCollection(String zkHost, String collection) {
        if (collection == null) {
            throw new IllegalArgumentException("collection must not be null");
        }
        SolrZkClient zkClient = getZkClient(zkHost);

        try (ZkStateReader zkStateReader = new ZkStateReader(zkClient)) {
            try {
                // first check for alias
                collection = checkForAlias(zkClient, collection);
                zkStateReader.createClusterStateWatchersAndUpdate();
            } catch (Exception e) {
                throw new IllegalArgumentException(
                        "Cannot find expected information for SolrCloud in ZooKeeper: " + zkHost, e);
            }

            try {
                return zkStateReader.getClusterState().getCollection(collection);
            } catch (SolrException e) {
                throw new IllegalArgumentException(
                        "Cannot find collection '" + collection + "' in ZooKeeper: " + zkHost, e);
            }
        } finally {
            zkClient.close();
        }
    }

    public SolrZkClient getZkClient(String zkHost) {
        if (zkHost == null) {
            throw new IllegalArgumentException("zkHost must not be null");
        }

        SolrZkClient zkClient;
        try {
            zkClient = new SolrZkClient(zkHost, 30000);
        } catch (Exception e) {
            throw new IllegalArgumentException("Cannot connect to ZooKeeper: " + zkHost, e);
        }
        return zkClient;
    }

    public List<Slice> getSortedSlices(Collection<Slice> slices) {
        List<Slice> sorted = new ArrayList(slices);
        Collections.sort(sorted, (slice1, slice2) -> {
            Comparator c = new AlphaNumericComparator();
            return c.compare(slice1.getName(), slice2.getName());
        });
        LOG.trace("Sorted slices: {}", sorted);
        return sorted;
    }

    /**
     * Returns config value given collection name
     * Borrowed heavily from Solr's ZKController.
     */
    public String readConfigName(SolrZkClient zkClient, String collection)
            throws KeeperException, InterruptedException {
        if (collection == null) {
            throw new IllegalArgumentException("collection must not be null");
        }
        String configName = null;

        // first check for alias
        collection = checkForAlias(zkClient, collection);

        String path = ZkStateReader.COLLECTIONS_ZKNODE + "/" + collection;
        if (LOG.isInfoEnabled()) {
            LOG.info("Load collection config from:" + path);
        }
        byte[] data = zkClient.getData(path, null, null, true);

        if (data != null) {
            ZkNodeProps props = ZkNodeProps.load(data);
            configName = props.getStr(ZkController.CONFIGNAME_PROP);
        }

        if (configName != null && !zkClient.exists(ZkConfigManager.CONFIGS_ZKNODE + "/" + configName, true)) {
            LOG.error("Specified config does not exist in ZooKeeper:" + configName);
            throw new IllegalArgumentException("Specified config does not exist in ZooKeeper:" + configName);
        }

        return configName;
    }

    private String checkForAlias(SolrZkClient zkClient, String collection)
            throws KeeperException, InterruptedException {
        byte[] aliasData = zkClient.getData(ZkStateReader.ALIASES, null, null, true);
        Aliases aliases = ClusterState.load(aliasData);
        String alias = aliases.getCollectionAlias(collection);
        if (alias != null) {
            List<String> aliasList = StrUtils.splitSmart(alias, ",", true);
            if (aliasList.size() > 1) {
                throw new IllegalArgumentException(
                        "collection cannot be an alias that maps to multiple collections");
            }
            collection = aliasList.get(0);
        }
        return collection;
    }

    /**
     * Download and return the config directory from ZK
     */
    public File downloadConfigDir(SolrZkClient zkClient, String configName)
            throws IOException, InterruptedException, KeeperException {
        File dir = Files.createTempDir();
        dir.deleteOnExit();
        ZkConfigManager configManager = new ZkConfigManager(zkClient);
        configManager.downloadConfigDir(configName, dir.toPath());
        File confDir = new File(dir, "conf");
        if (!confDir.isDirectory()) {
            // create a temporary directory with "conf" subdir and mv the config in there.  This is
            // necessary because of CDH-11188; solrctl does not generate nor accept directories with e.g.
            // conf/solrconfig.xml which is necessary for proper solr operation.  This should work
            // even if solrctl changes.
            confDir = new File(Files.createTempDir().getAbsolutePath(), "conf");
            confDir.getParentFile().deleteOnExit();
            Files.move(dir, confDir);
            dir = confDir.getParentFile();
        }
        FileUtils.writeStringToFile(new File(dir, "solr.xml"), "<solr><solrcloud></solrcloud></solr>", "UTF-8");
        verifyConfigDir(confDir);
        return dir;
    }

    private void verifyConfigDir(File confDir) throws IOException {
        File solrConfigFile = new File(confDir, "solrconfig.xml");
        if (!solrConfigFile.exists()) {
            throw new IOException("Detected invalid Solr config dir in ZooKeeper - Reason: File not found: "
                    + solrConfigFile.getName());
        }
        if (!solrConfigFile.isFile()) {
            throw new IOException("Detected invalid Solr config dir in ZooKeeper - Reason: Not a file: "
                    + solrConfigFile.getName());
        }
        if (!solrConfigFile.canRead()) {
            throw new IOException("Insufficient permissions to read file: " + solrConfigFile);
        }
    }

}