org.apache.hadoop.hdfs.server.hightidenode.ConfigManager.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.hdfs.server.hightidenode.ConfigManager.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.hdfs.server.hightidenode;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.HashSet;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.SAXException;

import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hdfs.protocol.PolicyInfo;
import org.apache.hadoop.hdfs.protocol.PolicyInfo.PathInfo;

/**
 * Maintains the configuration xml file that is read into memory.
 */
class ConfigManager {
    public static final Log LOG = LogFactory.getLog("org.apache.hadoop.hdfs.server.hightidenode.ConfigManager");

    /** Time to wait between checks of the config file */
    public static final long RELOAD_INTERVAL = 10 * 1000;

    /** Time to wait between successive runs of all policies */
    public static final long RESCAN_INTERVAL = 3600 * 1000;

    /**
     * Time to wait after the config file has been modified before reloading it
     * (this is done to prevent loading a file that hasn't been fully written).
     */
    public static final long RELOAD_WAIT = 5 * 1000;

    private Configuration conf; // Hadoop configuration
    private String configFileName; // Path to config XML file

    private long lastReloadAttempt; // Last time we tried to reload the config file
    private long lastSuccessfulReload; // Last time we successfully reloaded config
    private boolean lastReloadAttemptFailed = false;
    private long reloadInterval = RELOAD_INTERVAL;
    private long periodicity; // time between runs of all policies

    // Reload the configuration
    private boolean doReload;
    private Thread reloadThread;
    private volatile boolean running = false;

    // Collection of all configured policies.
    Collection<PolicyInfo> allPolicies = new ArrayList<PolicyInfo>();

    public ConfigManager(Configuration conf) throws IOException, SAXException, HighTideConfigurationException,
            ClassNotFoundException, ParserConfigurationException {
        this.conf = conf;
        this.configFileName = conf.get("hightide.config.file");
        this.doReload = conf.getBoolean("hightide.config.reload", true);
        this.reloadInterval = conf.getLong("hightide.config.reload.interval", RELOAD_INTERVAL);
        if (configFileName == null) {
            String msg = "No hightide.config.file given in conf - "
                    + "the Hadoop HighTideNode cannot run. Aborting....";
            LOG.warn(msg);
            throw new IOException(msg);
        }
        reloadConfigs();
        lastSuccessfulReload = HighTideNode.now();
        lastReloadAttempt = HighTideNode.now();
        running = true;
    }

    /**
     * Reload config file if it hasn't been loaded in a while
     * Returns true if the file was reloaded.
     */
    public synchronized boolean reloadConfigsIfNecessary() {
        long time = HighTideNode.now();
        if (time > lastReloadAttempt + reloadInterval) {
            lastReloadAttempt = time;
            File file = null;
            try {
                file = new File(configFileName);
                long lastModified = file.lastModified();
                if (lastModified > lastSuccessfulReload && time > lastModified + RELOAD_WAIT) {
                    reloadConfigs();
                    lastSuccessfulReload = time;
                    lastReloadAttemptFailed = false;
                    return true;
                }
            } catch (Exception e) {
                if (!lastReloadAttemptFailed) {
                    LOG.error("Failed to reload config file - " + file + "will use existing configuration.", e);
                }
                lastReloadAttemptFailed = true;
            }
        }
        return false;
    }

    /**
     * Updates the in-memory data structures from the config file. This file is
     * expected to be in the following whitespace-separated format:
     * Blank lines and lines starting with # are ignored.
     *  
     * @throws IOException if the config file cannot be read.
     * @throws HighTideConfigurationException if configuration entries are invalid.
     * @throws ClassNotFoundException if user-defined policy classes cannot be loaded
     * @throws ParserConfigurationException if XML parser is misconfigured.
     * @throws SAXException if config file is malformed.
     * @returns A new set of policy categories.
     */
    void reloadConfigs() throws IOException, ParserConfigurationException, SAXException, ClassNotFoundException,
            HighTideConfigurationException {

        if (configFileName == null) {
            return;
        }

        File file = new File(configFileName);
        if (!file.exists()) {
            throw new HighTideConfigurationException("Configuration file " + configFileName + " does not exist.");
        }

        // Read and parse the configuration file.
        // allow include files in configuration file
        DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
        docBuilderFactory.setIgnoringComments(true);
        docBuilderFactory.setNamespaceAware(true);
        try {
            docBuilderFactory.setXIncludeAware(true);
        } catch (UnsupportedOperationException e) {
            LOG.error("Failed to set setXIncludeAware(true) for raid parser " + docBuilderFactory + ":" + e, e);
        }
        LOG.error("Reloading config file " + file);

        DocumentBuilder builder = docBuilderFactory.newDocumentBuilder();
        Document doc = builder.parse(file);
        Element root = doc.getDocumentElement();
        if (!"configuration".equalsIgnoreCase(root.getTagName()))
            throw new HighTideConfigurationException(
                    "Bad configuration file: " + "top-level element not <configuration>");
        NodeList elements = root.getChildNodes();

        Set<PolicyInfo> existingPolicies = new HashSet<PolicyInfo>();

        // loop through all the configured source paths.
        for (int i = 0; i < elements.getLength(); i++) {
            Node node = elements.item(i);
            if (!(node instanceof Element)) {
                continue;
            }
            Element element = (Element) node;
            String elementTagName = element.getTagName();
            String policyName = null;
            if ("srcPath".equalsIgnoreCase(elementTagName)) {
                String srcPathPrefix = element.getAttribute("name");

                if (srcPathPrefix == null || srcPathPrefix.length() == 0) {
                    throw new HighTideConfigurationException(
                            "Bad configuration file: " + "srcPath node does not have a path.");
                }
                PolicyInfo policyInfo = new PolicyInfo(srcPathPrefix, conf);
                policyName = srcPathPrefix;
                Properties policyProperties;

                // loop through all elements of this policy
                NodeList policies = element.getChildNodes();
                for (int j = 0; j < policies.getLength(); j++) {
                    Node node1 = policies.item(j);
                    if (!(node1 instanceof Element)) {
                        continue;
                    }
                    Element policy = (Element) node1;
                    if ((!"property".equalsIgnoreCase(policy.getTagName()))
                            && (!"destPath".equalsIgnoreCase(policy.getTagName()))) {
                        throw new HighTideConfigurationException(
                                "Bad configuration file: " + "Expecting <property> or <destPath> for srcPath "
                                        + srcPathPrefix + " but found " + policy.getTagName());
                    }

                    // parse the <destPath> items
                    if ("destPath".equalsIgnoreCase(policy.getTagName())) {
                        String destPath = policy.getAttribute("name");
                        if (destPath == null) {
                            throw new HighTideConfigurationException("Bad configuration file: "
                                    + "<destPath> tag should have an attribute named 'name'.");
                        }
                        NodeList properties = policy.getChildNodes();
                        Properties destProperties = new Properties();
                        for (int k = 0; k < properties.getLength(); k++) {
                            Node node2 = properties.item(k);
                            if (!(node2 instanceof Element)) {
                                continue;
                            }
                            Element property = (Element) node2;
                            String propertyName = property.getTagName();
                            if (!("property".equalsIgnoreCase(propertyName))) {
                                throw new HighTideConfigurationException(
                                        "Bad configuration file: " + "<destPath> can have only <property> children."
                                                + " but found " + propertyName);
                            }
                            NodeList nl = property.getChildNodes();
                            String pname = null, pvalue = null;
                            for (int l = 0; l < nl.getLength(); l++) {
                                Node node3 = nl.item(l);
                                if (!(node3 instanceof Element)) {
                                    continue;
                                }
                                Element item = (Element) node3;
                                String itemName = item.getTagName();
                                if ("name".equalsIgnoreCase(itemName)) {
                                    pname = ((Text) item.getFirstChild()).getData().trim();
                                } else if ("value".equalsIgnoreCase(itemName)) {
                                    pvalue = ((Text) item.getFirstChild()).getData().trim();
                                }
                            }
                            if (pname == null || pvalue == null) {
                                throw new HighTideConfigurationException("Bad configuration file: "
                                        + "All property for destPath " + destPath + "  must have name and value ");
                            }
                            LOG.info(policyName + "." + pname + " = " + pvalue);
                            destProperties.setProperty(pname, pvalue);
                        }
                        policyInfo.addDestPath(destPath, destProperties);

                    } else if ("property".equalsIgnoreCase(policy.getTagName())) {
                        Element property = (Element) node1;
                        NodeList nl = property.getChildNodes();
                        String pname = null, pvalue = null;
                        for (int l = 0; l < nl.getLength(); l++) {
                            Node node3 = nl.item(l);
                            if (!(node3 instanceof Element)) {
                                continue;
                            }
                            Element item = (Element) node3;
                            String itemName = item.getTagName();
                            if ("name".equalsIgnoreCase(itemName)) {
                                pname = ((Text) item.getFirstChild()).getData().trim();
                            } else if ("value".equalsIgnoreCase(itemName)) {
                                pvalue = ((Text) item.getFirstChild()).getData().trim();
                            }
                        }
                        if (pname == null || pvalue == null) {
                            throw new HighTideConfigurationException("Bad configuration file: "
                                    + "All property for srcPath " + srcPathPrefix + " must have name and value ");
                        }
                        LOG.info(policyName + "." + pname + " = " + pvalue);
                        policyInfo.setProperty(pname, pvalue);
                    }
                }
                existingPolicies.add(policyInfo);
            } else {
                throw new HighTideConfigurationException("Bad configuration file: "
                        + "The top level item must be srcPath but found " + elementTagName);
            }
        }
        validateAllPolicies(existingPolicies);
        setAllPolicies(existingPolicies);
        return;
    }

    /**
     * Get a collection of all policies
     */
    public synchronized Collection<PolicyInfo> getAllPolicies() {
        return allPolicies;
    }

    /**
     * Set a collection of all policies
     */
    protected synchronized void setAllPolicies(Collection<PolicyInfo> value) {
        this.allPolicies = value;
    }

    /**
     * Validate a collection of policies
     */
    private void validateAllPolicies(Collection<PolicyInfo> all) throws IOException, NumberFormatException {
        for (PolicyInfo pinfo : all) {
            Path srcPath = pinfo.getSrcPath();
            if (srcPath == null) {
                throw new IOException("Unable to find srcPath in policy.");
            }
            if (pinfo.getProperty("replication") == null) {
                throw new IOException("Unable to find replication in policy." + srcPath);
            }
            int repl = Integer.parseInt(pinfo.getProperty("replication"));
            if (pinfo.getProperty("modTimePeriod") == null) {
                throw new IOException("Unable to find modTimePeriod in policy." + srcPath);
            }
            long value = Long.parseLong(pinfo.getProperty("modTimePeriod"));
            List<PathInfo> dpaths = pinfo.getDestPaths();
            if (dpaths == null || dpaths.size() == 0) {
                throw new IOException("Unable to find dest in policy." + srcPath);
            }
            for (PathInfo pp : dpaths) {
                if (pp.getPath() == null) {
                    throw new IOException("Unable to find valid destPath in policy " + srcPath);
                }
                if (pp.getProperty("replication") == null) {
                    throw new IOException("Unable to find dest replication in policy." + srcPath);
                }
                repl = Integer.parseInt(pp.getProperty("replication"));
            }
        }
    }

    /**
     * Start a background thread to reload the config file
     */
    void startReload() {
        if (doReload) {
            reloadThread = new UpdateThread();
            reloadThread.start();
        }
    }

    /**
     * Stop the background thread that reload the config file
     */
    void stopReload() throws InterruptedException {
        if (reloadThread != null) {
            running = false;
            reloadThread.interrupt();
            reloadThread.join();
            reloadThread = null;
        }
    }

    /**
     * A thread which reloads the config file.
     */
    private class UpdateThread extends Thread {
        private UpdateThread() {
            super("HighTideNode config reload thread");
        }

        public void run() {
            while (running) {
                try {
                    Thread.sleep(reloadInterval);
                    reloadConfigsIfNecessary();
                } catch (InterruptedException e) {
                    // do nothing
                } catch (Exception e) {
                    LOG.error("Failed to reload config file ", e);
                }
            }
        }
    }

}