org.apache.hadoop.registry.client.binding.RegistryUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.registry.client.binding.RegistryUtils.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.client.binding;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import org.apache.commons.lang3.StringUtils;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.fs.PathNotFoundException;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.registry.client.api.RegistryConstants;
import org.apache.hadoop.registry.client.api.RegistryOperations;
import org.apache.hadoop.registry.client.exceptions.InvalidPathnameException;
import org.apache.hadoop.registry.client.exceptions.InvalidRecordException;
import org.apache.hadoop.registry.client.exceptions.NoRecordException;
import org.apache.hadoop.registry.client.impl.zk.RegistryInternalConstants;
import org.apache.hadoop.registry.client.types.RegistryPathStatus;
import org.apache.hadoop.registry.client.types.ServiceRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static org.apache.hadoop.registry.client.binding.RegistryPathUtils.*;

import java.io.EOFException;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

/**
 * Utility methods for working with a registry.
 */
@InterfaceAudience.Public
@InterfaceStability.Evolving
public class RegistryUtils {
    private static final Logger LOG = LoggerFactory.getLogger(RegistryUtils.class);

    /**
     * Buld the user path -switches to the system path if the user is "".
     * It also cross-converts the username to ascii via punycode
     * @param username username or ""
     * @return the path to the user
     */
    public static String homePathForUser(String username) {
        Preconditions.checkArgument(username != null, "null user");

        // catch recursion
        if (username.startsWith(RegistryConstants.PATH_USERS)) {
            return username;
        }
        if (username.isEmpty()) {
            return RegistryConstants.PATH_SYSTEM_SERVICES;
        }

        // convert username to registry name
        String convertedName = convertUsername(username);

        return RegistryPathUtils.join(RegistryConstants.PATH_USERS, encodeForRegistry(convertedName));
    }

    /**
     * Convert the username to that which can be used for registry
     * entries. Lower cases it,
     * Strip the kerberos realm off a username if needed, and any "/" hostname
     * entries
     * @param username user
     * @return the converted username
     */
    public static String convertUsername(String username) {
        String converted = org.apache.hadoop.util.StringUtils.toLowerCase(username);
        int atSymbol = converted.indexOf('@');
        if (atSymbol > 0) {
            converted = converted.substring(0, atSymbol);
        }
        int slashSymbol = converted.indexOf('/');
        if (slashSymbol > 0) {
            converted = converted.substring(0, slashSymbol);
        }
        return converted;
    }

    /**
     * Create a service classpath
     * @param user username or ""
     * @param serviceClass service name
     * @return a full path
     */
    public static String serviceclassPath(String user, String serviceClass) {
        String services = join(homePathForUser(user), RegistryConstants.PATH_USER_SERVICES);
        return join(services, serviceClass);
    }

    /**
     * Create a path to a service under a user and service class
     * @param user username or ""
     * @param serviceClass service name
     * @param serviceName service name unique for that user and service class
     * @return a full path
     */
    public static String servicePath(String user, String serviceClass, String serviceName) {

        return join(serviceclassPath(user, serviceClass), serviceName);
    }

    /**
     * Create a path for listing components under a service
     * @param user username or ""
     * @param serviceClass service name
     * @param serviceName service name unique for that user and service class
     * @return a full path
     */
    public static String componentListPath(String user, String serviceClass, String serviceName) {

        return join(servicePath(user, serviceClass, serviceName), RegistryConstants.SUBPATH_COMPONENTS);
    }

    /**
     * Create the path to a service record for a component
     * @param user username or ""
     * @param serviceClass service name
     * @param serviceName service name unique for that user and service class
     * @param componentName unique name/ID of the component
     * @return a full path
     */
    public static String componentPath(String user, String serviceClass, String serviceName, String componentName) {

        return join(componentListPath(user, serviceClass, serviceName), componentName);
    }

    /**
     * List service records directly under a path
     * @param registryOperations registry operations instance
     * @param path path to list
     * @return a mapping of the service records that were resolved, indexed
     * by their full path
     * @throws IOException
     */
    public static Map<String, ServiceRecord> listServiceRecords(RegistryOperations registryOperations, String path)
            throws IOException {
        Map<String, RegistryPathStatus> children = statChildren(registryOperations, path);
        return extractServiceRecords(registryOperations, path, children.values());
    }

    /**
     * List children of a directory and retrieve their
     * {@link RegistryPathStatus} values.
     * <p>
     * This is not an atomic operation; A child may be deleted
     * during the iteration through the child entries. If this happens,
     * the <code>PathNotFoundException</code> is caught and that child
     * entry ommitted.
     *
     * @param path path
     * @return a possibly empty map of child entries listed by
     * their short name.
     * @throws PathNotFoundException path is not in the registry.
     * @throws InvalidPathnameException the path is invalid.
     * @throws IOException Any other IO Exception
     */
    public static Map<String, RegistryPathStatus> statChildren(RegistryOperations registryOperations, String path)
            throws PathNotFoundException, InvalidPathnameException, IOException {
        List<String> childNames = registryOperations.list(path);
        Map<String, RegistryPathStatus> results = new HashMap<String, RegistryPathStatus>();
        for (String childName : childNames) {
            String child = join(path, childName);
            try {
                RegistryPathStatus stat = registryOperations.stat(child);
                results.put(childName, stat);
            } catch (PathNotFoundException pnfe) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("stat failed on {}: moved? {}", child, pnfe, pnfe);
                }
                // and continue
            }
        }
        return results;
    }

    /**
     * Get the home path of the current user.
     * <p>
     *  In an insecure cluster, the environment variable
     *  <code>HADOOP_USER_NAME</code> is queried <i>first</i>.
     * <p>
     * This means that in a YARN container where the creator set this
     * environment variable to propagate their identity, the defined
     * user name is used in preference to the actual user.
     * <p>
     * In a secure cluster, the kerberos identity of the current user is used.
     * @return a path for the current user's home dir.
     * @throws RuntimeException if the current user identity cannot be determined
     * from the OS/kerberos.
     */
    public static String homePathForCurrentUser() {
        String shortUserName = currentUsernameUnencoded();
        return homePathForUser(shortUserName);
    }

    /**
     * Get the current username, before any encoding has been applied.
     * @return the current user from the kerberos identity, falling back
     * to the user and/or env variables.
     */
    private static String currentUsernameUnencoded() {
        String env_hadoop_username = System.getenv(RegistryInternalConstants.HADOOP_USER_NAME);
        return getCurrentUsernameUnencoded(env_hadoop_username);
    }

    /**
     * Get the current username, using the value of the parameter
     * <code>env_hadoop_username</code> if it is set on an insecure cluster.
     * This ensures that the username propagates correctly across processes
     * started by YARN.
     * <p>
     * This method is primarly made visible for testing.
     * @param env_hadoop_username the environment variable
     * @return the selected username
     * @throws RuntimeException if there is a problem getting the short user
     * name of the current user.
     */
    @VisibleForTesting
    public static String getCurrentUsernameUnencoded(String env_hadoop_username) {
        String shortUserName = null;
        if (!UserGroupInformation.isSecurityEnabled()) {
            shortUserName = env_hadoop_username;
        }
        if (StringUtils.isEmpty(shortUserName)) {
            try {
                shortUserName = UserGroupInformation.getCurrentUser().getShortUserName();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return shortUserName;
    }

    /**
     * Get the current user path formatted for the registry
     * <p>
     *  In an insecure cluster, the environment variable
     *  <code>HADOOP_USER_NAME </code> is queried <i>first</i>.
     * <p>
     * This means that in a YARN container where the creator set this
     * environment variable to propagate their identity, the defined
     * user name is used in preference to the actual user.
     * <p>
     * In a secure cluster, the kerberos identity of the current user is used.
     * @return the encoded shortname of the current user
     * @throws RuntimeException if the current user identity cannot be determined
     * from the OS/kerberos.
     *
     */
    public static String currentUser() {
        String shortUserName = currentUsernameUnencoded();
        return registryUser(shortUserName);
    }

    /**
     * Convert the given user name formatted for the registry.
     *
     * @param shortUserName
     * @return converted user name
     */
    public static String registryUser(String shortUserName) {
        String encodedName = encodeForRegistry(shortUserName);
        // DNS name doesn't allow "_", replace it with "-"
        encodedName = RegistryUtils.convertUsername(encodedName);
        return encodedName.replace("_", "-");
    }

    /**
     * Extract all service records under a list of stat operations...this
     * skips entries that are too short or simply not matching
     * @param operations operation support for fetches
     * @param parentpath path of the parent of all the entries
     * @param stats Collection of stat results
     * @return a possibly empty map of fullpath:record.
     * @throws IOException for any IO Operation that wasn't ignored.
     */
    public static Map<String, ServiceRecord> extractServiceRecords(RegistryOperations operations, String parentpath,
            Collection<RegistryPathStatus> stats) throws IOException {
        Map<String, ServiceRecord> results = new HashMap<String, ServiceRecord>(stats.size());
        for (RegistryPathStatus stat : stats) {
            if (stat.size > ServiceRecord.RECORD_TYPE.length()) {
                // maybe has data
                String path = join(parentpath, stat.path);
                try {
                    ServiceRecord serviceRecord = operations.resolve(path);
                    results.put(path, serviceRecord);
                } catch (EOFException ignored) {
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("data too short for {}", path);
                    }
                } catch (InvalidRecordException record) {
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("Invalid record at {}", path);
                    }
                } catch (NoRecordException record) {
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("No record at {}", path);
                    }
                }
            }
        }
        return results;
    }

    /**
     * Extract all service records under a list of stat operations...this
     * non-atomic action skips entries that are too short or simply not matching.
     * <p>
     * @param operations operation support for fetches
     * @param parentpath path of the parent of all the entries
     * @return a possibly empty map of fullpath:record.
     * @throws IOException for any IO Operation that wasn't ignored.
     */
    public static Map<String, ServiceRecord> extractServiceRecords(RegistryOperations operations, String parentpath,
            Map<String, RegistryPathStatus> stats) throws IOException {
        return extractServiceRecords(operations, parentpath, stats.values());
    }

    /**
     * Extract all service records under a list of stat operations...this
     * non-atomic action skips entries that are too short or simply not matching.
     * <p>
     * @param operations operation support for fetches
     * @param parentpath path of the parent of all the entries
     * @return a possibly empty map of fullpath:record.
     * @throws IOException for any IO Operation that wasn't ignored.
     */
    public static Map<String, ServiceRecord> extractServiceRecords(RegistryOperations operations, String parentpath)
            throws IOException {
        return extractServiceRecords(operations, parentpath, statChildren(operations, parentpath).values());
    }

    /**
     * Static instance of service record marshalling
     */
    public static class ServiceRecordMarshal extends JsonSerDeser<ServiceRecord> {
        public ServiceRecordMarshal() {
            super(ServiceRecord.class);
        }
    }
}