org.rhq.modules.plugins.jbossas7.ConfigurationWriteDelegate.java Source code

Java tutorial

Introduction

Here is the source code for org.rhq.modules.plugins.jbossas7.ConfigurationWriteDelegate.java

Source

/*
 * RHQ Management Platform
 * Copyright (C) 2005-2011 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.modules.plugins.jbossas7;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

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.ConfigurationUpdateStatus;
import org.rhq.core.domain.configuration.Property;
import org.rhq.core.domain.configuration.PropertyList;
import org.rhq.core.domain.configuration.PropertyMap;
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.PropertyDefinitionList;
import org.rhq.core.domain.configuration.definition.PropertyDefinitionMap;
import org.rhq.core.domain.configuration.definition.PropertyDefinitionSimple;
import org.rhq.core.domain.configuration.definition.PropertyGroupDefinition;
import org.rhq.core.domain.configuration.definition.PropertySimpleType;
import org.rhq.core.pluginapi.configuration.ConfigurationFacet;
import org.rhq.core.pluginapi.configuration.ConfigurationUpdateReport;
import org.rhq.modules.plugins.jbossas7.json.Address;
import org.rhq.modules.plugins.jbossas7.json.CompositeOperation;
import org.rhq.modules.plugins.jbossas7.json.Operation;
import org.rhq.modules.plugins.jbossas7.json.ReadChildrenResources;
import org.rhq.modules.plugins.jbossas7.json.Remove;
import org.rhq.modules.plugins.jbossas7.json.Result;
import org.rhq.modules.plugins.jbossas7.json.WriteAttribute;

public class ConfigurationWriteDelegate implements ConfigurationFacet {

    final Log log = LogFactory.getLog(this.getClass());

    private Address _address;
    private ASConnection connection;
    private ConfigurationDefinition configurationDefinition;
    private String namePropLocator;
    private String type;
    private boolean addNewChildren;
    private boolean addDeleteModifiedChildren;

    /**
     * Create a new configuration delegate, that reads the attributes for the resource at address.
     * @param configDef Configuration definition for the configuration
     * @param connection asConnection to use
     * @param address address of the resource.
     */
    public ConfigurationWriteDelegate(ConfigurationDefinition configDef, ASConnection connection, Address address) {
        this.configurationDefinition = configDef;
        this.connection = connection;
        this._address = address;
    }

    /**
     * Trigger loading of a configuration by talking to the remote resource.
     * @return The initialized configuration
     * @throws Exception If anything goes wrong.
     */
    public Configuration loadResourceConfiguration() throws Exception {

        throw new IllegalAccessException("Please use ConfigurationLoadDelegate");

    }

    /**
     * Write the configuration back to the AS. Care must be taken, not to send properties that
     * are read-only, as AS will choke on them.
     * @param report Report containing the new configuration
     */
    public void updateResourceConfiguration(ConfigurationUpdateReport report) {

        Configuration conf = report.getConfiguration();

        CompositeOperation cop = updateGenerateOperationFromProperties(conf, _address);

        Result result = connection.execute(cop);
        if (!result.isSuccess()) {
            report.setStatus(ConfigurationUpdateStatus.FAILURE);
            report.setErrorMessage(result.getFailureDescription());
        } else {
            report.setStatus(ConfigurationUpdateStatus.SUCCESS);
            // TODO how to signal "need reload"
        }

    }

    protected CompositeOperation updateGenerateOperationFromProperties(Configuration conf, Address address) {

        CompositeOperation cop = new CompositeOperation();

        for (PropertyDefinition propDef : configurationDefinition.getNonGroupedProperties()) {

            updateProperty(conf, cop, propDef, address);
        }
        for (PropertyGroupDefinition pgd : configurationDefinition.getGroupDefinitions()) {
            String groupName = pgd.getName();
            namePropLocator = null;
            if (groupName.startsWith("children:")) { // children, where the key in key=value from the path is known
                type = groupName.substring("children:".length());
                if (type.contains(":")) {
                    namePropLocator = type.substring(type.indexOf(":") + 1);
                    if (namePropLocator.endsWith("+")) { // ending in +  -> we need to :add new entries
                        namePropLocator = namePropLocator.substring(0, namePropLocator.length() - 1);
                        addNewChildren = true;
                    } else if (namePropLocator.endsWith("+-")) { // ending in +-  -> we need to :add new entries and remove/add to modify
                        namePropLocator = namePropLocator.substring(0, namePropLocator.length() - 2);
                        addNewChildren = true;
                        addDeleteModifiedChildren = true;
                    } else {
                        addNewChildren = false;
                    }
                    type = type.substring(0, type.indexOf(":"));
                } else {
                    log.error("Group name " + groupName + " contains no property name locator ");
                    return cop;
                }
                List<PropertyDefinition> definitions = configurationDefinition.getPropertiesInGroup(groupName);
                for (PropertyDefinition def : definitions) {
                    updateProperty(conf, cop, def, address);
                }
            }
            if (groupName.startsWith("child:")) { // one named child resource
                String subPath = groupName.substring("child:".length());
                if (!subPath.contains("="))
                    throw new IllegalArgumentException("subPath of 'child:' expression has no =");

                Address address1 = new Address(address);
                address1.addSegment(subPath);

                List<PropertyDefinition> definitions = configurationDefinition.getPropertiesInGroup(groupName);
                for (PropertyDefinition def : definitions) {
                    updateProperty(conf, cop, def, address1);
                }

            } // TODO handle attribute: case
        }

        return cop;
    }

    private void updateProperty(Configuration conf, CompositeOperation cop, PropertyDefinition propDef,
            Address baseAddress) {

        // Skip over read-only properties, the AS can not use them anyway
        if (propDef.isReadOnly())
            return;

        // Handle the special case
        String propDefName = propDef.getName();
        if (propDef instanceof PropertyDefinitionList && propDefName.startsWith("*")) {
            propDef = ((PropertyDefinitionList) propDef).getMemberDefinition();
            PropertyList pl = (PropertyList) conf.get(propDefName);

            // check if we need to see if that property exists - get the current state of affairs from the AS
            List<String> existingPropnames = new ArrayList<String>();
            if (addNewChildren) {
                Operation op = new ReadChildrenResources(baseAddress, type);
                Result tmp = connection.execute(op);
                if (tmp.isSuccess()) {
                    Map<String, Object> tmpResMap = (Map<String, Object>) tmp.getResult();
                    existingPropnames.addAll(tmpResMap.keySet());
                }
            }

            // Loop over the list - i.e. the individual rows that come from the server
            for (Property prop2 : pl.getList()) {
                updateHandlePropertyMapSpecial(cop, (PropertyMap) prop2, (PropertyDefinitionMap) propDef,
                        baseAddress, existingPropnames);
            }
            // now check about removed properties
            for (String existingName : existingPropnames) {
                boolean found = false;
                for (Property prop2 : pl.getList()) {
                    PropertyMap propMap2 = (PropertyMap) prop2;
                    String itemName = propMap2.getSimple(namePropLocator).getStringValue();
                    if (itemName == null) {
                        throw new IllegalArgumentException(
                                "Map contains no entry with name [" + namePropLocator + "]");
                    }
                    if (itemName.equals(existingName)) {
                        found = true;
                        break;
                    }
                }

                if (!found) {
                    Address tmpAddr = new Address(baseAddress);
                    tmpAddr.add(type, existingName);
                    Operation operation = new Operation("remove", tmpAddr);
                    cop.addStep(operation);
                }
            }

        } else {
            // Normal cases
            Property prop = conf.get(propDefName);

            if (prop instanceof PropertySimple && propDef instanceof PropertyDefinitionSimple) {
                updateHandlePropertySimple(cop, (PropertySimple) prop, (PropertyDefinitionSimple) propDef,
                        baseAddress);
            } else if (prop instanceof PropertyList && propDef instanceof PropertyDefinitionList) {
                updateHandlePropertyList(cop, (PropertyList) prop, (PropertyDefinitionList) propDef, baseAddress);
            } else if (prop instanceof PropertyMap && propDef instanceof PropertyDefinitionMap) {
                updateHandlePropertyMap(cop, (PropertyMap) prop, (PropertyDefinitionMap) propDef, baseAddress);
            } else {
                String s = "Property and definition are not matching:\n";
                s += "Property: " + prop + "\n";
                s += "PropDef : " + propDef;
                throw new IllegalArgumentException(s);
            }
        }
    }

    private void updateHandlePropertyMap(CompositeOperation cop, PropertyMap prop, PropertyDefinitionMap propDef,
            Address address) {

        String propName = prop.getName();
        Operation writeAttribute;
        Map<String, Object> results;
        if (propName.endsWith(":collapsed")) {
            String realName = propName.substring(0, propName.indexOf(':'));
            results = handleCollapsedMap(prop, propDef);
            writeAttribute = new WriteAttribute(address, realName, results);
        } else {
            results = updateHandleMap(prop, propDef, address);
            writeAttribute = new WriteAttribute(address, propName, results);
        }
        cop.addStep(writeAttribute);
    }

    private Map<String, Object> handleCollapsedMap(PropertyMap propertyMap,
            PropertyDefinitionMap propertyDefinitionMap) {

        String first = null;
        String second = null;
        for (Map.Entry<String, PropertyDefinition> entry : propertyDefinitionMap.getPropertyDefinitions()
                .entrySet()) {
            PropertyDefinition def = entry.getValue();
            if (!def.getName().contains(":"))
                throw new IllegalArgumentException("Member names in a :collapsed map must end in :0 and :1");

            Property prop = propertyMap.get(def.getName());
            if (prop == null) {
                throw new IllegalArgumentException("Property " + def.getName() + " was null - must not happen");
            }

            PropertySimple ps = (PropertySimple) prop;
            if (def.getName().endsWith(":0"))
                first = ps.getStringValue();
            else if (def.getName().endsWith(":1"))
                second = ps.getStringValue(); // TODO other types?
            else
                throw new IllegalArgumentException("Member names in a :collapsed map must end in :0 and :1");
        }

        Map<String, Object> resultMap = new HashMap<String, Object>();
        resultMap.put(first, second);

        return resultMap;
    }

    private void updateHandlePropertyMapSpecial(CompositeOperation cop, PropertyMap prop,
            PropertyDefinitionMap propDef, Address address, List<String> existingPropNames) {
        Map<String, Object> results = updateHandleMap(prop, propDef, address);
        if (prop.get(namePropLocator) == null) {
            throw new IllegalArgumentException("There is no element in the map with the name " + namePropLocator);
        }
        String key = ((PropertySimple) prop.get(namePropLocator)).getStringValue();

        Operation operation;
        Address addr = new Address(address);
        addr.add(type, key);

        if (!addNewChildren || existingPropNames.contains(key)) {
            // update existing entry
            if (addDeleteModifiedChildren) {

                operation = new Remove(addr);
                cop.addStep(operation);

                operation = new Operation("add", addr);
                for (Map.Entry<String, Object> entry : results.entrySet()) {
                    String key1 = entry.getKey();
                    Object value = getValueWithType(entry, propDef);
                    if (key1.endsWith(":expr")) {
                        key1 = key1.substring(0, key1.indexOf(':'));
                        Map<String, Object> tmp = new HashMap<String, Object>();
                        tmp.put("EXPRESSION_VALUE", value);

                        operation.addAdditionalProperty(key1, tmp);
                    } else {
                        operation.addAdditionalProperty(key1, value);
                    }
                }
                cop.addStep(operation);
            } else {
                for (Map.Entry<String, Object> entry : results.entrySet()) {
                    String key1 = entry.getKey();
                    Object value = getValueWithType(entry, propDef);
                    if (key1.endsWith(":expr")) {
                        key1 = key1.substring(0, key1.indexOf(':'));
                        Map<String, Object> tmp = new HashMap<String, Object>();
                        tmp.put("EXPRESSION_VALUE", value);
                        operation = new WriteAttribute(addr, key1, tmp);
                    } else {
                        operation = new WriteAttribute(addr, key1, value);
                    }
                    cop.addStep(operation);
                }
            }

        } else {
            // write new child ( :name+ case )
            operation = new Operation("add", addr);
            for (Map.Entry<String, Object> entry : results.entrySet()) {
                String key1 = entry.getKey();
                Object value = getValueWithType(entry, propDef);
                if (key1.endsWith(":expr")) {
                    key1 = key1.substring(0, key1.indexOf(':'));
                    Map<String, Object> tmp = new HashMap<String, Object>();
                    tmp.put("EXPRESSION_VALUE", value);
                    operation.addAdditionalProperty(key1, tmp);
                } else {
                    operation.addAdditionalProperty(key1, value);
                }
            }
            cop.addStep(operation);
        }
    }

    private void updateHandlePropertyList(CompositeOperation cop, PropertyList prop, PropertyDefinitionList propDef,
            Address address) {
        PropertyDefinition memberDef = propDef.getMemberDefinition();

        // We need to collect the list members, create an array and attach this to the cop

        List<Property> embeddedProps = prop.getList();
        List<Object> values = new ArrayList<Object>();
        for (Property inner : embeddedProps) {
            if (memberDef instanceof PropertyDefinitionSimple) {
                PropertySimple ps = (PropertySimple) inner;
                if (ps.getStringValue() != null)
                    values.add(ps.getStringValue()); // TODO handling of optional vs required

            }
            if (memberDef instanceof PropertyDefinitionMap) {
                Map<String, Object> mapResult = updateHandleMap((PropertyMap) inner,
                        (PropertyDefinitionMap) memberDef, address);
                values.add(mapResult);
            }
        }
        Operation writeAttribute = new WriteAttribute(address, prop.getName(), values);
        cop.addStep(writeAttribute);
    }

    private void updateHandlePropertySimple(CompositeOperation cop, PropertySimple propertySimple,
            PropertyDefinitionSimple propDef, Address address) {

        // If the property value is null and the property is optional, skip too
        if (propertySimple.getStringValue() == null && !propDef.isRequired())
            return;

        Operation writeAttribute = new WriteAttribute(address);
        String name = propertySimple.getName();
        if (name.endsWith(":expr")) {

            String realName = name.substring(0, name.indexOf(":"));
            try {
                Integer num = Integer.parseInt(propertySimple.getStringValue());
                writeAttribute.addAdditionalProperty("name", realName);
                writeAttribute.addAdditionalProperty("value", propertySimple.getStringValue());
            } catch (NumberFormatException nfe) {
                // Not a number, and expressions are allowed, so send an expression
                Map<String, String> expr = new HashMap<String, String>(1);
                expr.put("EXPRESSION_VALUE", propertySimple.getStringValue());
                writeAttribute.addAdditionalProperty("name", realName);
                writeAttribute.addAdditionalProperty("value", expr);
            }
        } else {
            writeAttribute.addAdditionalProperty("name", name);
            writeAttribute.addAdditionalProperty("value", propertySimple.getStringValue());
        }
        cop.addStep(writeAttribute);
    }

    private Map<String, Object> updateHandleMap(PropertyMap map, PropertyDefinitionMap mapDef, Address address) {
        Map<String, PropertyDefinition> memberDefinitions = mapDef.getPropertyDefinitions();

        Map<String, Object> results = new HashMap<String, Object>();
        for (String name : memberDefinitions.keySet()) {
            PropertyDefinition memberDefinition = memberDefinitions.get(name);

            if (memberDefinition.isReadOnly())
                continue;

            if (memberDefinition instanceof PropertyDefinitionSimple) {
                PropertyDefinitionSimple pds = (PropertyDefinitionSimple) memberDefinition;
                PropertySimple ps = (PropertySimple) map.get(name);
                if ((ps == null || ps.getStringValue() == null) && !pds.isRequired())
                    continue;
                if (ps != null)
                    results.put(name, ps.getStringValue());
            } else {
                log.error(" *** not yet supported *** : " + memberDefinition.getName());
            }
        }
        return results;
    }

    private Object getValueWithType(Map.Entry<String, Object> entry, PropertyDefinitionMap definitions) {

        PropertyDefinitionSimple pds = (PropertyDefinitionSimple) definitions.get(entry.getKey());
        if (!(entry.getValue() instanceof String)) {
            return entry.getValue();
        }

        String val = (String) entry.getValue();
        PropertySimpleType type = pds.getType();
        Object ret;
        switch (type) {
        case STRING:
            ret = val;
            break;
        case INTEGER:
            ret = Integer.valueOf(val);
            break;
        case BOOLEAN:
            ret = Boolean.valueOf(val);
            break;
        case LONG:
            ret = Long.valueOf(val);
            break;
        case FLOAT:
            ret = Float.valueOf(val);
            break;
        case DOUBLE:
            ret = Double.valueOf(val);
            break;
        default:
            ret = val;
        }

        return ret;
    }

}