/**
 * 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.ambari.server.controller.internal;

import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.ClusterNotFoundException;
import org.apache.ambari.server.ConfigGroupNotFoundException;
import org.apache.ambari.server.DuplicateResourceException;
import org.apache.ambari.server.HostNotFoundException;
import org.apache.ambari.server.ParentObjectNotFoundException;
import org.apache.ambari.server.controller.AmbariManagementController;
import org.apache.ambari.server.controller.ConfigGroupRequest;
import org.apache.ambari.server.controller.ConfigGroupResponse;
import org.apache.ambari.server.controller.spi.NoSuchParentResourceException;
import org.apache.ambari.server.controller.spi.NoSuchResourceException;
import org.apache.ambari.server.controller.spi.Predicate;
import org.apache.ambari.server.controller.spi.Request;
import org.apache.ambari.server.controller.spi.RequestStatus;
import org.apache.ambari.server.controller.spi.Resource;
import org.apache.ambari.server.controller.spi.ResourceAlreadyExistsException;
import org.apache.ambari.server.controller.spi.ResourcePredicateEvaluator;
import org.apache.ambari.server.controller.spi.SystemException;
import org.apache.ambari.server.controller.spi.UnsupportedPropertyException;
import org.apache.ambari.server.controller.utilities.PropertyHelper;
import org.apache.ambari.server.state.Cluster;
import org.apache.ambari.server.state.Clusters;
import org.apache.ambari.server.state.Config;
import org.apache.ambari.server.state.ConfigImpl;
import org.apache.ambari.server.state.Host;
import org.apache.ambari.server.state.configgroup.ConfigGroup;
import org.apache.ambari.server.state.configgroup.ConfigGroupFactory;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class ConfigGroupResourceProvider extends
  AbstractControllerResourceProvider implements ResourcePredicateEvaluator {

  private static final Logger configLogger = LoggerFactory.getLogger("configchange");
  private static final Logger LOG = LoggerFactory.getLogger
    (ConfigGroupResourceProvider.class);

  protected static final String CONFIGGROUP_CLUSTER_NAME_PROPERTY_ID =
    PropertyHelper.getPropertyId("ConfigGroup", "cluster_name");
  protected static final String CONFIGGROUP_ID_PROPERTY_ID = PropertyHelper
    .getPropertyId("ConfigGroup", "id");
  protected static final String CONFIGGROUP_NAME_PROPERTY_ID = PropertyHelper
    .getPropertyId("ConfigGroup", "group_name");
  protected static final String CONFIGGROUP_TAG_PROPERTY_ID = PropertyHelper
    .getPropertyId("ConfigGroup", "tag");
  protected static final String CONFIGGROUP_DESC_PROPERTY_ID = PropertyHelper
    .getPropertyId("ConfigGroup", "description");
  protected static final String CONFIGGROUP_SCV_NOTE_ID = PropertyHelper
      .getPropertyId("ConfigGroup", "service_config_version_note");
  protected static final String CONFIGGROUP_HOSTNAME_PROPERTY_ID =
    PropertyHelper.getPropertyId(null, "host_name");
  protected static final String CONFIGGROUP_HOSTS_HOSTNAME_PROPERTY_ID =
    PropertyHelper.getPropertyId("ConfigGroup", "hosts/host_name");
  public static final String CONFIGGROUP_HOSTS_PROPERTY_ID = PropertyHelper
    .getPropertyId("ConfigGroup", "hosts");
  public static final String CONFIGGROUP_CONFIGS_PROPERTY_ID =
    PropertyHelper.getPropertyId("ConfigGroup", "desired_configs");

  private static Set<String> pkPropertyIds = new HashSet<String>(Arrays
    .asList(new String[] { CONFIGGROUP_ID_PROPERTY_ID }));

  /**
   * Create a  new resource provider for the given management controller.
   *
   * @param propertyIds          the property ids
   * @param keyPropertyIds       the key property ids
   * @param managementController the management controller
   */
  protected ConfigGroupResourceProvider(Set<String> propertyIds,
       Map<Resource.Type, String> keyPropertyIds,
       AmbariManagementController managementController) {
    super(propertyIds, keyPropertyIds, managementController);
  }

  @Override
  protected Set<String> getPKPropertyIds() {
    return pkPropertyIds;
  }

  @Override
  public RequestStatus createResources(Request request) throws
       SystemException, UnsupportedPropertyException,
       ResourceAlreadyExistsException, NoSuchParentResourceException {

    final Set<ConfigGroupRequest> requests = new HashSet<ConfigGroupRequest>();
    for (Map<String, Object> propertyMap : request.getProperties()) {
      requests.add(getConfigGroupRequest(propertyMap));
    }
    RequestStatus status = createResources(requests);
    notifyCreate(Resource.Type.ConfigGroup, request);
    return status;
  }

  @Override
  public Set<Resource> getResources(Request request, Predicate predicate) throws
       SystemException, UnsupportedPropertyException, NoSuchResourceException,
       NoSuchParentResourceException {

    final Set<ConfigGroupRequest> requests = new HashSet<ConfigGroupRequest>();
    for (Map<String, Object> propertyMap : getPropertyMaps(predicate)) {
      requests.add(getConfigGroupRequest(propertyMap));
    }

    Set<ConfigGroupResponse> responses = getResources(new Command<Set<ConfigGroupResponse>>() {
      @Override
      public Set<ConfigGroupResponse> invoke() throws AmbariException {
        return getConfigGroups(requests);
      }
    });

    Set<String>   requestedIds = getRequestPropertyIds(request, predicate);
    Set<Resource> resources    = new HashSet<Resource>();

    if (requestedIds.contains(CONFIGGROUP_HOSTS_HOSTNAME_PROPERTY_ID)) {
      requestedIds.add(CONFIGGROUP_HOSTS_PROPERTY_ID);
    }

    for (ConfigGroupResponse response : responses) {
      Resource resource = new ResourceImpl(Resource.Type.ConfigGroup);

      setResourceProperty(resource, CONFIGGROUP_ID_PROPERTY_ID,
        response.getId(), requestedIds);
      setResourceProperty(resource, CONFIGGROUP_CLUSTER_NAME_PROPERTY_ID,
        response.getClusterName(), requestedIds);
      setResourceProperty(resource, CONFIGGROUP_NAME_PROPERTY_ID,
        response.getGroupName(), requestedIds);
      setResourceProperty(resource, CONFIGGROUP_TAG_PROPERTY_ID,
        response.getTag(), requestedIds);
      setResourceProperty(resource, CONFIGGROUP_DESC_PROPERTY_ID,
        response.getDescription(), requestedIds);
      setResourceProperty(resource, CONFIGGROUP_HOSTS_PROPERTY_ID,
        response.getHosts(), requestedIds);
      setResourceProperty(resource, CONFIGGROUP_CONFIGS_PROPERTY_ID,
        response.getConfigurations(), requestedIds);

      resources.add(resource);
    }

    return resources;
  }

  @Override
  public RequestStatus updateResources(Request request, Predicate predicate) throws
       SystemException, UnsupportedPropertyException,
       NoSuchResourceException, NoSuchParentResourceException {

    final Set<ConfigGroupRequest> requests = new HashSet<ConfigGroupRequest>();

    Iterator<Map<String,Object>> iterator = request.getProperties().iterator();
    if (iterator.hasNext()) {
      for (Map<String, Object> propertyMap : getPropertyMaps(iterator.next(), predicate)) {
        requests.add(getConfigGroupRequest(propertyMap));
      }
    }

    RequestStatus status = updateResources(requests);

    notifyUpdate(Resource.Type.ConfigGroup, request, predicate);

    return status;
  }

  @Override
  public RequestStatus deleteResources(Predicate predicate) throws
       SystemException, UnsupportedPropertyException, NoSuchResourceException,
       NoSuchParentResourceException {

    for (Map<String, Object> propertyMap : getPropertyMaps(predicate)) {
      final ConfigGroupRequest configGroupRequest = getConfigGroupRequest(propertyMap);

      modifyResources(new Command<Void>() {
        @Override
        public Void invoke() throws AmbariException {
          deleteConfigGroup(configGroupRequest);
          return null;
        }
      });
    }

    notifyDelete(Resource.Type.ConfigGroup, predicate);

    return getRequestStatus(null);
  }

  @Override
  public Set<String> checkPropertyIds(Set<String> propertyIds) {
    //allow providing service_config_version_note, but we should not return it for config group
    Set<String> unsupportedPropertyIds = super.checkPropertyIds(propertyIds);
    for (Iterator<String> iterator = unsupportedPropertyIds.iterator(); iterator.hasNext(); ) {
      String next = iterator.next();
      next = PropertyHelper.getPropertyName(next);
      if (next.equals("service_config_version_note") || next.equals("/service_config_version_note")) {
        iterator.remove();
      }
    }
    return unsupportedPropertyIds;
  }

  /**
   * Create configuration group resources based on set of config group requests.
   *
   * @param requests  set of config group requests
   *
   * @return a request status
   *
   * @throws SystemException                an internal system exception occurred
   * @throws UnsupportedPropertyException   the request contains unsupported property ids
   * @throws ResourceAlreadyExistsException attempted to create a resource which already exists
   * @throws NoSuchParentResourceException  a parent resource of the resource to create doesn't exist
   */
  public RequestStatus createResources(final Set<ConfigGroupRequest> requests)throws
      SystemException, UnsupportedPropertyException,
      ResourceAlreadyExistsException, NoSuchParentResourceException{

    Set<ConfigGroupResponse> responses =
        createResources(new Command<Set<ConfigGroupResponse>>() {
          @Override
          public Set<ConfigGroupResponse> invoke() throws AmbariException {
            return createConfigGroups(requests);
          }
        });

    Set<Resource> associatedResources = new HashSet<Resource>();
    for (ConfigGroupResponse response : responses) {
      Resource resource = new ResourceImpl(Resource.Type.ConfigGroup);
      resource.setProperty(CONFIGGROUP_ID_PROPERTY_ID, response.getId());
      associatedResources.add(resource);
    }

    return getRequestStatus(null, associatedResources);
  }

  public RequestStatus updateResources(final Set<ConfigGroupRequest> requests)
      throws SystemException,
      UnsupportedPropertyException,
      NoSuchResourceException,
      NoSuchParentResourceException {

    modifyResources(new Command<Void>() {
      @Override
      public Void invoke() throws AmbariException {
        updateConfigGroups(requests);
        return null;
      }
    });

    return getRequestStatus(null);
  }

  private synchronized  Set<ConfigGroupResponse> getConfigGroups
    (Set<ConfigGroupRequest> requests) throws AmbariException {
    Set<ConfigGroupResponse> responses = new HashSet<ConfigGroupResponse>();
    if (requests != null) {
      for (ConfigGroupRequest request : requests) {
        LOG.debug("Received a Config group request with"
          + ", clusterName = " + request.getClusterName()
          + ", groupId = " + request.getId()
          + ", groupName = " + request.getGroupName()
          + ", tag = " + request.getTag());

        if (request.getClusterName() == null) {
          LOG.warn("Cluster name is a required field.");
          continue;
        }

        Cluster cluster = getManagementController().getClusters().getCluster
          (request.getClusterName());
        Map<Long, ConfigGroup> configGroupMap = cluster.getConfigGroups();

        // By group id
        if (request.getId() != null) {
          ConfigGroup configGroup = configGroupMap.get(request.getId());
          if (configGroup != null) {
            responses.add(configGroup.convertToResponse());
          } else {
            throw new ConfigGroupNotFoundException(cluster.getClusterName(),
              request.getId().toString());
          }
          continue;
        }
        // By group name
        if (request.getGroupName() != null) {
          for (ConfigGroup configGroup : configGroupMap.values()) {
            if (configGroup.getName().equals(request.getGroupName())) {
              responses.add(configGroup.convertToResponse());
            }
          }
          continue;
        }
        // By tag only
        if (request.getTag() != null && request.getHosts().isEmpty()) {
          for (ConfigGroup configGroup : configGroupMap.values()) {
            if (configGroup.getTag().equals(request.getTag())) {
              responses.add(configGroup.convertToResponse());
            }
          }
          continue;
        }
        // By hostnames only
        if (!request.getHosts().isEmpty() && request.getTag() == null) {
          for (String hostname : request.getHosts()) {
            Map<Long, ConfigGroup> groupMap = cluster
              .getConfigGroupsByHostname(hostname);

            if (!groupMap.isEmpty()) {
              for (ConfigGroup configGroup : groupMap.values()) {
                responses.add(configGroup.convertToResponse());
              }
            }
          }
          continue;
        }
        // By tag and hostnames
        if (request.getTag() != null && !request.getHosts().isEmpty()) {
          for (ConfigGroup configGroup : configGroupMap.values()) {
            // Has tag
            if (configGroup.getTag().equals(request.getTag())) {
              // Has a match with hosts
              Set<String> groupHosts = new HashSet<String>(configGroup
                .getHosts().keySet());
              groupHosts.retainAll(request.getHosts());
              if (!groupHosts.isEmpty()) {
                responses.add(configGroup.convertToResponse());
              }
            }
          }
          continue;
        }
        // Select all
        for (ConfigGroup configGroup : configGroupMap.values()) {
          responses.add(configGroup.convertToResponse());
        }
      }
    }
    return responses;
  }

  private void verifyHostList(Cluster cluster, Map<String, Host> hosts,
                              ConfigGroupRequest request) throws AmbariException {

    Map<Long, ConfigGroup> configGroupMap = cluster.getConfigGroups();

    if (configGroupMap != null) {
      for (ConfigGroup configGroup : configGroupMap.values()) {
        if (configGroup.getTag().equals(request.getTag())
            && !configGroup.getId().equals(request.getId())) {
          // Check the new host list for duplicated with this group
          for (Host host : hosts.values()) {
            if (configGroup.getHosts().containsKey(host.getHostName())) {
              throw new DuplicateResourceException("Host is already " +
                "associated with a config group"
                + ", clusterName = " + configGroup.getClusterName()
                + ", configGroupName = " + configGroup.getName()
                + ", tag = " + configGroup.getTag()
                + ", hostname = " + host.getHostName());
            }
          }
        }
      }
    }
  }

  private synchronized void deleteConfigGroup(ConfigGroupRequest request)
    throws AmbariException {
    if (request.getId() == null) {
      throw new AmbariException("Config group id is a required field.");
    }

    Clusters clusters = getManagementController().getClusters();

    Cluster cluster;
    try {
      cluster = clusters.getCluster(request.getClusterName());
    } catch (ClusterNotFoundException e) {
      throw new ParentObjectNotFoundException(
        "Attempted to delete a config group from a cluster which doesn't " +
          "exist", e);
    }

    configLogger.info("Deleting Config group, "
      + ", clusterName = " + cluster.getClusterName()
      + ", id = " + request.getId()
      + ", user = " + getManagementController().getAuthName());

    cluster.deleteConfigGroup(request.getId());
  }

  private void validateRequest(ConfigGroupRequest request) {
    if (request.getClusterName() == null
      || request.getClusterName().isEmpty()
      || request.getGroupName() == null
      || request.getGroupName().isEmpty()
      || request.getTag() == null
      || request.getTag().isEmpty()) {

      LOG.debug("Received a config group request with cluster name = " +
        request.getClusterName() + ", group name = " + request.getGroupName()
        + ", tag = " + request.getTag());

      throw new IllegalArgumentException("Cluster name, " +
        "group name and tag need to be provided.");

    }
  }

  private synchronized Set<ConfigGroupResponse> createConfigGroups
    (Set<ConfigGroupRequest> requests) throws AmbariException {

    if (requests.isEmpty()) {
      LOG.warn("Received an empty requests set");
      return null;
    }

    Set<ConfigGroupResponse> configGroupResponses = new
      HashSet<ConfigGroupResponse>();

    Clusters clusters = getManagementController().getClusters();
    ConfigGroupFactory configGroupFactory = getManagementController()
      .getConfigGroupFactory();

    for (ConfigGroupRequest request : requests) {

      Cluster cluster;
      try {
        cluster = clusters.getCluster(request.getClusterName());
      } catch (ClusterNotFoundException e) {
        throw new ParentObjectNotFoundException(
          "Attempted to add a config group to a cluster which doesn't exist", e);
      }

      validateRequest(request);

      Map<Long, ConfigGroup> configGroupMap = cluster.getConfigGroups();
      if (configGroupMap != null) {
        for (ConfigGroup configGroup : configGroupMap.values()) {
          if (configGroup.getName().equals(request.getGroupName()) &&
              configGroup.getTag().equals(request.getTag())) {
            throw new DuplicateResourceException("Config group already " +
              "exists with the same name and tag"
              + ", clusterName = " + request.getClusterName()
              + ", groupName = " + request.getGroupName()
              + ", tag = " + request.getTag());
          }
        }
      }

      // Find hosts
      Map<String, Host> hosts = new HashMap<String, Host>();
      if (request.getHosts() != null && !request.getHosts().isEmpty()) {
        for (String hostname : request.getHosts()) {
          Host host = clusters.getHost(hostname);
          if (host == null) {
            throw new HostNotFoundException(hostname);
          }
          hosts.put(hostname, host);
        }
      }

      verifyHostList(cluster, hosts, request);

      ConfigGroup configGroup = configGroupFactory.createNew(cluster,
        request.getGroupName(),
        request.getTag(), request.getDescription(),
        request.getConfigs(), hosts);

      String serviceName = null;
      if (request.getConfigs() != null && !request.getConfigs().isEmpty()) {
        serviceName = cluster.getServiceForConfigTypes(request.getConfigs().keySet());
      }
      configGroup.setServiceName(serviceName);

      // Persist before add, since id is auto-generated
      configLogger.info("Persisting new Config group"
        + ", clusterName = " + cluster.getClusterName()
        + ", name = " + configGroup.getName()
        + ", tag = " + configGroup.getTag()
        + ", user = " + getManagementController().getAuthName());

      configGroup.persist();
      cluster.addConfigGroup(configGroup);
      if (serviceName != null) {
        cluster.createServiceConfigVersion(serviceName, getManagementController().getAuthName(),
          request.getServiceConfigVersionNote(), configGroup);
      } else {
        LOG.warn("Could not determine service name for config group {}, service config version not created",
            configGroup.getId());
      }

      ConfigGroupResponse response = new ConfigGroupResponse(configGroup
        .getId(), configGroup.getClusterName(), configGroup.getName(),
        configGroup.getTag(), configGroup.getDescription(), null, null);

      configGroupResponses.add(response);
    }

    return configGroupResponses;
  }

  private synchronized void updateConfigGroups
    (Set<ConfigGroupRequest> requests) throws AmbariException {

    if (requests.isEmpty()) {
      LOG.warn("Received an empty requests set");
      return;
    }

    Clusters clusters = getManagementController().getClusters();

    for (ConfigGroupRequest request : requests) {

      Cluster cluster;
      try {
        cluster = clusters.getCluster(request.getClusterName());
      } catch (ClusterNotFoundException e) {
        throw new ParentObjectNotFoundException(
          "Attempted to add a config group to a cluster which doesn't exist", e);
      }

      if (request.getId() == null) {
        throw new AmbariException("Config group Id is a required parameter.");
      }

      validateRequest(request);

      // Find config group
      ConfigGroup configGroup = cluster.getConfigGroups().get(request.getId());
      if (configGroup == null) {
        throw new AmbariException("Config group not found"
                                 + ", clusterName = " + request.getClusterName()
                                 + ", groupId = " + request.getId());
      }
      String serviceName = configGroup.getServiceName();
      String requestServiceName = cluster.getServiceForConfigTypes(request.getConfigs().keySet());
      if (serviceName != null && requestServiceName !=null && !StringUtils.equals(serviceName, requestServiceName)) {
        throw new IllegalArgumentException("Config group " + configGroup.getId() +
            " is mapped to service " + serviceName + ", " +
            "but request contain configs from service " + requestServiceName);
      } else if (serviceName == null && requestServiceName != null) {
        configGroup.setServiceName(requestServiceName);
        serviceName = requestServiceName;
      }

      // Update hosts
      Map<String, Host> hosts = new HashMap<String, Host>();
      if (request.getHosts() != null && !request.getHosts().isEmpty()) {
        for (String hostname : request.getHosts()) {
          Host host = clusters.getHost(hostname);
          if (host == null) {
            throw new HostNotFoundException(hostname);
          }
          hosts.put(hostname, host);
        }
      }

      verifyHostList(cluster, hosts, request);

      configGroup.setHosts(hosts);

      // Update Configs
      configGroup.setConfigurations(request.getConfigs());

      // Save
      configGroup.setName(request.getGroupName());
      configGroup.setDescription(request.getDescription());
      configGroup.setTag(request.getTag());

      configLogger.info("Persisting updated Config group"
        + ", clusterName = " + configGroup.getClusterName()
        + ", id = " + configGroup.getId()
        + ", tag = " + configGroup.getTag()
        + ", user = " + getManagementController().getAuthName());

      configGroup.persist();
      if (serviceName != null) {
        cluster.createServiceConfigVersion(serviceName, getManagementController().getAuthName(),
          request.getServiceConfigVersionNote(), configGroup);
      } else {
        LOG.warn("Could not determine service name for config group {}, service config version not created",
            configGroup.getId());
      }
    }

    getManagementController().getConfigHelper().invalidateStaleConfigsCache();
  }

  @SuppressWarnings("unchecked")
  ConfigGroupRequest getConfigGroupRequest(Map<String, Object> properties) {
    Object groupIdObj = properties.get(CONFIGGROUP_ID_PROPERTY_ID);
    Long groupId = null;
    if (groupIdObj != null)  {
      groupId = groupIdObj instanceof Long ? (Long) groupIdObj :
        Long.parseLong((String) groupIdObj);
    }

    ConfigGroupRequest request = new ConfigGroupRequest(
      groupId,
      (String) properties.get(CONFIGGROUP_CLUSTER_NAME_PROPERTY_ID),
      (String) properties.get(CONFIGGROUP_NAME_PROPERTY_ID),
      (String) properties.get(CONFIGGROUP_TAG_PROPERTY_ID),
      (String) properties.get(CONFIGGROUP_DESC_PROPERTY_ID),
      null,
      null);

    request.setServiceConfigVersionNote((String) properties.get(CONFIGGROUP_SCV_NOTE_ID));

    Map<String, Config> configurations = new HashMap<String, Config>();
    Set<String> hosts = new HashSet<String>();

    String hostnameKey = CONFIGGROUP_HOSTNAME_PROPERTY_ID;
    Object hostObj = properties.get(CONFIGGROUP_HOSTS_PROPERTY_ID);
    if (hostObj == null) {
      hostnameKey = CONFIGGROUP_HOSTS_HOSTNAME_PROPERTY_ID;
      hostObj = properties.get(CONFIGGROUP_HOSTS_HOSTNAME_PROPERTY_ID);
    }
    if (hostObj != null) {
      if (hostObj instanceof HashSet<?>) {
        try {
          Set<Map<String, String>> hostsSet = (Set<Map<String, String>>) hostObj;
          for (Map<String, String> hostMap : hostsSet) {
            if (hostMap.containsKey(hostnameKey)) {
              String hostname = hostMap.get(hostnameKey);
              hosts.add(hostname);
            }
          }
        } catch (Exception e) {
          LOG.warn("Host json in unparseable format. " + hostObj, e);
        }
      } else {
        if (hostObj instanceof String) {
          hosts.add((String) hostObj);
        }
      }
    }

    Object configObj = properties.get(CONFIGGROUP_CONFIGS_PROPERTY_ID);
    if (configObj != null && configObj instanceof HashSet<?>) {
      try {
        Set<Map<String, Object>> configSet = (Set<Map<String, Object>>) configObj;
        for (Map<String, Object> configMap : configSet) {
          String type = (String) configMap.get(ConfigurationResourceProvider
            .CONFIGURATION_CONFIG_TYPE_PROPERTY_ID);
          String tag = (String) configMap.get(ConfigurationResourceProvider
            .CONFIGURATION_CONFIG_TAG_PROPERTY_ID);

          Map<String, String> configProperties = new HashMap<String, String>();
          Map<String, Map<String, String>> configAttributes = new HashMap<String, Map<String, String>>();

          for (Map.Entry<String, Object> entry : configMap.entrySet()) {
            String propertyCategory = PropertyHelper.getPropertyCategory(entry.getKey());
            if (propertyCategory != null && entry.getValue() != null) {
              if ("properties".equals(propertyCategory)) {
                configProperties.put(PropertyHelper.getPropertyName(entry.getKey()),
                    entry.getValue().toString());
              } else if ("properties_attributes".equals(PropertyHelper.getPropertyCategory(propertyCategory))) {
                String attributeName = PropertyHelper.getPropertyName(propertyCategory);
                if (!configAttributes.containsKey(attributeName)) {
                  configAttributes.put(attributeName, new HashMap<String, String>());
                }
                Map<String, String> attributeValues
                    = configAttributes.get(attributeName);
                attributeValues.put(PropertyHelper.getPropertyName(entry.getKey()),
                    entry.getValue().toString());
              }
            }
          }

          Config config = new ConfigImpl(type);
          config.setTag(tag);
          config.setProperties(configProperties);
          config.setPropertiesAttributes(configAttributes);

          configurations.put(config.getType(), config);
        }
      } catch (Exception e) {
        LOG.warn("Config json in unparseable format. " + configObj, e);
      }
    }

    request.setConfigs(configurations);
    request.setHosts(hosts);

    return request;
  }

  /**
   * Bypassing predicate evaluation for the lack of a matcher for a
   * non-scalar resource
   *
   * @param predicate  the predicate
   * @param resource   the resource
   *
   * @return always returns true
   */
  @Override
  public boolean evaluate(Predicate predicate, Resource resource) {
    return true;
  }
}
