org.rhq.plugins.hadoop.HadoopServerConfigurationDelegate.java Source code

Java tutorial

Introduction

Here is the source code for org.rhq.plugins.hadoop.HadoopServerConfigurationDelegate.java

Source

/*
 * RHQ Management Platform
 * Copyright (C) 2005-2012 Red Hat, Inc.
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

package org.rhq.plugins.hadoop;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.Property;
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.domain.configuration.definition.ConfigurationDefinition;
import org.rhq.core.domain.configuration.definition.PropertyDefinition;
import org.rhq.core.domain.configuration.definition.PropertyDefinitionSimple;
import org.rhq.core.pluginapi.inventory.ResourceComponent;
import org.rhq.core.pluginapi.inventory.ResourceContext;
import org.rhq.core.util.file.FileUtil;

/**
 * 
 *
 * @author Lukas Krejci
 */
public class HadoopServerConfigurationDelegate {

    private static final Log LOG = LogFactory.getLog(HadoopServerConfigurationDelegate.class);

    private static final XMLInputFactory XML_INPUT_FACTORY = XMLInputFactory.newInstance();
    private static final XMLOutputFactory XML_OUTPUT_FACTORY = XMLOutputFactory.newInstance();

    private static final String CONFIGURATION_TAG_NAME = "configuration";
    private static final String PROPERTY_TAG_NAME = "property";
    private static final String NAME_TAG_NAME = "name";
    private static final String VALUE_TAG_NAME = "value";

    private static final Pattern PROPERTY_NAME_EXTRACT_PATTERN = Pattern.compile("<name>(.*)<\\/name>");
    private static final Pattern PROPERTY_VALUE_REPLACE_PATTERN = Pattern.compile("<value>.*<\\/value>");

    private static class DetectedPropertyNameAndUpdatedTag {
        String propertyName;
        String updatedTag;
    }

    private static class PropertiesPerConfigFileBuilder {
        private static class ConfigFileAndConfigName {
            File configFile;
            String propertyName;

            ConfigFileAndConfigName(File homeDir, String propertyDefinitionName) {
                String[] parts = propertyDefinitionName.split(":");

                String configFileName = parts[0];

                configFile = new File(homeDir, configFileName);
                propertyName = parts[1];

                if (!configFile.exists()) {
                    throw new IllegalArgumentException("The expected configuration file ("
                            + configFile.getAbsolutePath() + ") doesn't exist.");
                }
            }
        }

        private Map<File, Map<String, PropertySimple>> propertiesPerFile = new HashMap<File, Map<String, PropertySimple>>();
        private File homeDir;

        public PropertiesPerConfigFileBuilder(File homeDir) {
            this.homeDir = homeDir;
        }

        void addProperty(String name, Configuration parentConfig) {
            PropertySimple ret = new PropertySimple();
            ret.setName(name);
            parentConfig.put(ret);
            addProperty(ret);
        }

        void addProperty(PropertySimple property) {
            ConfigFileAndConfigName fn = new ConfigFileAndConfigName(homeDir, property.getName());

            Map<String, PropertySimple> props = propertiesPerFile.get(fn.configFile);
            if (props == null) {
                props = new HashMap<String, PropertySimple>();
                propertiesPerFile.put(fn.configFile, props);
            }

            props.put(fn.propertyName, property);
        }

        Map<File, Map<String, PropertySimple>> getPropertiesPerFilePerConfigName() {
            return propertiesPerFile;
        }
    }

    private ResourceContext<ResourceComponent<?>> componentContext;

    public HadoopServerConfigurationDelegate(ResourceContext<ResourceComponent<?>> componentContext) {
        this.componentContext = componentContext;
    }

    public Configuration loadConfiguration() throws Exception {
        ConfigurationDefinition definition = componentContext.getResourceType()
                .getResourceConfigurationDefinition();
        Configuration config = new Configuration();

        File homeDir = getHomeDir();

        fillResourceConfiguration(homeDir, config, definition);

        return config;
    }

    public void updateConfiguration(Configuration config) throws Exception {
        //gather the files to update
        PropertiesPerConfigFileBuilder bld = new PropertiesPerConfigFileBuilder(getHomeDir());

        for (Property p : config.getProperties()) {
            if (!(p instanceof PropertySimple)) {
                continue;
            }

            PropertySimple property = (PropertySimple) p;
            bld.addProperty(property);
        }

        for (Map.Entry<File, Map<String, PropertySimple>> e : bld.getPropertiesPerFilePerConfigName().entrySet()) {
            updateFile(e.getKey(), e.getValue());
        }
    }

    private File getHomeDir() {
        File homeDir = new File(
                componentContext.getPluginConfiguration().getSimpleValue(HadoopServerDiscovery.HOME_DIR_PROPERTY));

        if (!homeDir.exists()) {
            throw new IllegalArgumentException("The configured home directory of this Hadoop instance ("
                    + homeDir.getAbsolutePath() + ") no longer exists.");
        }

        if (!homeDir.isDirectory()) {
            throw new IllegalArgumentException("The configured home directory of this Hadoop instance ("
                    + homeDir.getAbsolutePath() + ") is not a directory.");
        }

        if (!homeDir.canRead()) {
            throw new IllegalArgumentException("The configured home directory of this Hadoop instance ("
                    + homeDir.getAbsolutePath() + ") is not readable.");
        }

        return homeDir;
    }

    public static void fillResourceConfiguration(File homeDir, Configuration config,
            ConfigurationDefinition definition) throws XMLStreamException, IOException {
        //the config is just a bunch of simples, so this is rather easy.. no cumbersome traversal of property maps and lists

        PropertiesPerConfigFileBuilder bld = new PropertiesPerConfigFileBuilder(homeDir);

        for (PropertyDefinition pd : definition.getPropertyDefinitions().values()) {
            if (!(pd instanceof PropertyDefinitionSimple)) {
                //hmm... well, someone thought it's enough to change the config and the code would be clever.
                //it's not ;)
                continue;
            }

            String propertyName = pd.getName();
            bld.addProperty(propertyName, config);
        }

        for (Map.Entry<File, Map<String, PropertySimple>> e : bld.getPropertiesPerFilePerConfigName().entrySet()) {
            File configFile = e.getKey();
            Map<String, PropertySimple> propertiesToFind = e.getValue();

            parseAndAssignProps(configFile, propertiesToFind);
        }
    }

    public static void parseAndAssignProps(File configFile, Map<String, PropertySimple> props)
            throws XMLStreamException, IOException {
        FileInputStream in = new FileInputStream(configFile);
        XMLStreamReader rdr = XML_INPUT_FACTORY.createXMLStreamReader(in);
        try {
            boolean inProperty = false;
            String propertyName = null;
            String propertyValue = null;

            while (rdr.hasNext()) {
                int event = rdr.next();

                String tag = null;

                switch (event) {
                case XMLStreamReader.START_ELEMENT:
                    tag = rdr.getName().getLocalPart();
                    if (PROPERTY_TAG_NAME.equals(tag)) {
                        inProperty = true;
                    } else if (inProperty && NAME_TAG_NAME.equals(tag)) {
                        propertyName = rdr.getElementText();
                    } else if (inProperty && VALUE_TAG_NAME.equals(tag)) {
                        propertyValue = rdr.getElementText();
                    }
                    break;
                case XMLStreamReader.END_ELEMENT:
                    tag = rdr.getName().getLocalPart();
                    if (PROPERTY_TAG_NAME.equals(tag)) {
                        inProperty = false;

                        PropertySimple prop = props.get(propertyName);
                        if (prop != null) {
                            prop.setValue(propertyValue);
                        }

                        propertyName = null;
                        propertyValue = null;
                    }
                    break;
                }
            }
        } finally {
            rdr.close();
            in.close();
        }
    }

    private static void updateFile(File configFile, Map<String, PropertySimple> allProps)
            throws IOException, InterruptedException, XMLStreamException {
        InputStream in = null;
        XMLStreamReader rdr = null;

        OutputStream out = null;
        XMLStreamWriter outWrt = null;

        try {
            Set<String> processedPropertyNames = new HashSet<String>();

            in = new BufferedInputStream(new FileInputStream(configFile));
            rdr = XML_INPUT_FACTORY.createXMLStreamReader(in);

            File tmpFile = File.createTempFile("hadoop-plugin", null);
            out = new FileOutputStream(tmpFile);
            outWrt = XML_OUTPUT_FACTORY.createXMLStreamWriter(out);

            ByteArrayOutputStream stash = new ByteArrayOutputStream();
            XMLStreamWriter stashWrt = XML_OUTPUT_FACTORY.createXMLStreamWriter(stash);
            boolean outputActive = true;

            outWrt.writeStartDocument();

            while (rdr.hasNext()) {
                int event = rdr.next();

                XMLStreamWriter wrt = outputActive ? outWrt : stashWrt;

                switch (event) {
                case XMLStreamConstants.ATTRIBUTE:
                    break;
                case XMLStreamConstants.CDATA:
                    wrt.writeCData(rdr.getText());
                    break;
                case XMLStreamConstants.CHARACTERS:
                    wrt.writeCharacters(rdr.getText());
                    break;
                case XMLStreamConstants.COMMENT:
                    wrt.writeComment(rdr.getText());
                    break;
                case XMLStreamConstants.DTD:
                    wrt.writeDTD(rdr.getText());
                    break;
                case XMLStreamConstants.END_DOCUMENT:
                    wrt.writeEndDocument();
                    break;
                case XMLStreamConstants.END_ELEMENT:
                    if (PROPERTY_TAG_NAME.equals(rdr.getName().getLocalPart())) {
                        String encoding = rdr.getEncoding();
                        if (encoding == null) {
                            encoding = "UTF-8";
                        }

                        String propertyTagSoFar = Charset.forName(encoding)
                                .decode(ByteBuffer.wrap(stash.toByteArray())).toString();
                        DetectedPropertyNameAndUpdatedTag propAndTag = updateProperty(propertyTagSoFar, allProps);

                        //yes, we're intentionally circumventing the xml stream writer, because we already have the XML data we want to write.
                        outWrt.flush();
                        out.write(propAndTag.updatedTag.getBytes("UTF-8"));

                        processedPropertyNames.add(propAndTag.propertyName);

                        //reset stuff
                        stash.reset();
                        wrt = outWrt;
                        outputActive = true;
                    } else if (CONFIGURATION_TAG_NAME.equals(rdr.getName().getLocalPart())) {
                        //now add the new props
                        for (String prop : processedPropertyNames) {
                            allProps.remove(prop);
                        }

                        for (Map.Entry<String, PropertySimple> e : allProps.entrySet()) {
                            outWrt.writeStartElement(PROPERTY_TAG_NAME);

                            outWrt.writeStartElement(NAME_TAG_NAME);
                            outWrt.writeCharacters(e.getKey());
                            outWrt.writeEndElement();

                            outWrt.writeStartElement(VALUE_TAG_NAME);
                            outWrt.writeCharacters(e.getValue().getStringValue());
                            outWrt.writeEndElement();

                            outWrt.writeEndElement();
                        }
                    }
                    wrt.writeEndElement();
                    break;
                case XMLStreamConstants.ENTITY_DECLARATION:
                    //XXX could not find what to do with this
                    break;
                case XMLStreamConstants.ENTITY_REFERENCE:
                    wrt.writeEntityRef(rdr.getText());
                    break;
                case XMLStreamConstants.NAMESPACE:
                    for (int i = 0; i < rdr.getNamespaceCount(); ++i) {
                        wrt.writeNamespace(rdr.getNamespacePrefix(i), rdr.getNamespaceURI(i));
                    }
                    break;
                case XMLStreamConstants.NOTATION_DECLARATION:
                    //XXX could not find what to do with this
                    break;
                case XMLStreamConstants.PROCESSING_INSTRUCTION:
                    wrt.writeProcessingInstruction(rdr.getPITarget(), rdr.getPIData());
                    break;
                case XMLStreamConstants.SPACE:
                    wrt.writeCharacters(rdr.getText());
                    break;
                case XMLStreamConstants.START_DOCUMENT:
                    //this seems to be never called for some strange reason
                    //wrt.writeStartDocument();
                    break;
                case XMLStreamConstants.START_ELEMENT:
                    wrt.writeStartElement(rdr.getName().getPrefix(), rdr.getName().getLocalPart(),
                            rdr.getName().getNamespaceURI());

                    for (int i = 0; i < rdr.getAttributeCount(); ++i) {
                        wrt.writeAttribute(rdr.getAttributePrefix(i), rdr.getAttributeNamespace(i),
                                rdr.getAttributeLocalName(i), rdr.getAttributeValue(i));
                    }

                    if (PROPERTY_TAG_NAME.equals(rdr.getName().getLocalPart())) {
                        wrt.writeCharacters("");
                        outputActive = false;
                    }
                    break;
                }
            }

            outWrt.flush();
            out.flush();
            out.close();

            in.close();

            //now copy the temp file in the place of the original one
            FileUtil.copyFile(tmpFile, configFile);
        } finally {
            rdr.close();

            outWrt.flush();
            outWrt.close();

            try {
                in.close();
            } finally {
                out.flush();
                out.close();
            }
        }
    }

    private static DetectedPropertyNameAndUpdatedTag updateProperty(String propertyTag,
            Map<String, PropertySimple> allProps) {

        DetectedPropertyNameAndUpdatedTag ret = new DetectedPropertyNameAndUpdatedTag();
        //for now
        ret.updatedTag = propertyTag;

        //extract the name of the property we're dealing with
        Matcher propertyNameMatcher = PROPERTY_NAME_EXTRACT_PATTERN.matcher(propertyTag);
        if (!propertyNameMatcher.find()) {
            return ret;
        }

        String propertyName = propertyNameMatcher.group(1);
        ret.propertyName = propertyName;

        if (propertyName == null) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("Possibly invalid property tag?\n" + propertyTag);
            }

            return ret;
        }

        //try to find the property in allProps
        PropertySimple prop = allProps.get(propertyName);

        if (prop == null) {
            return ret;
        }

        if (prop.getStringValue() == null) {
            //this property has been unset
            ret.updatedTag = "";
            return ret;
        }

        Matcher propertyValueMatcher = PROPERTY_VALUE_REPLACE_PATTERN.matcher(propertyTag);
        if (!propertyValueMatcher.find()) {
            return ret;
        }

        ret.updatedTag = propertyValueMatcher.replaceAll("<value>" + prop.getStringValue() + "</value>");

        return ret;
    }
}