/*
 * 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 static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.JDK_LOCATION;
import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.OVERRIDE_CONFIGS;
import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.OVERRIDE_STACK_NAME;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.StaticallyInject;
import org.apache.ambari.server.actionmanager.ActionManager;
import org.apache.ambari.server.actionmanager.RequestFactory;
import org.apache.ambari.server.actionmanager.Stage;
import org.apache.ambari.server.actionmanager.StageFactory;
import org.apache.ambari.server.api.services.AmbariMetaInfo;
import org.apache.ambari.server.configuration.Configuration;
import org.apache.ambari.server.controller.ActionExecutionContext;
import org.apache.ambari.server.controller.AmbariActionExecutionHelper;
import org.apache.ambari.server.controller.AmbariManagementController;
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.Resource.Type;
import org.apache.ambari.server.controller.spi.ResourceAlreadyExistsException;
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.orm.dao.HostVersionDAO;
import org.apache.ambari.server.orm.dao.RepositoryVersionDAO;
import org.apache.ambari.server.orm.entities.HostVersionEntity;
import org.apache.ambari.server.orm.entities.RepoOsEntity;
import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
import org.apache.ambari.server.stack.upgrade.RepositoryVersionHelper;
import org.apache.ambari.server.state.Cluster;
import org.apache.ambari.server.state.Host;
import org.apache.ambari.server.state.RepositoryVersionState;
import org.apache.ambari.server.state.ServiceComponentHost;
import org.apache.ambari.server.state.StackId;
import org.apache.ambari.server.utils.StageUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Sets;
import com.google.inject.Inject;
import com.google.inject.Provider;

/**
 * Resource provider for host stack versions resources.
 */
@StaticallyInject
public class HostStackVersionResourceProvider extends AbstractControllerResourceProvider {

  private static final Logger LOG = LoggerFactory.getLogger(HostStackVersionResourceProvider.class);

  // ----- Property ID constants ---------------------------------------------

  protected static final String HOST_STACK_VERSION_ID_PROPERTY_ID              = PropertyHelper.getPropertyId("HostStackVersions", "id");
  protected static final String HOST_STACK_VERSION_CLUSTER_NAME_PROPERTY_ID    = PropertyHelper.getPropertyId("HostStackVersions", "cluster_name");
  protected static final String HOST_STACK_VERSION_HOST_NAME_PROPERTY_ID       = PropertyHelper.getPropertyId("HostStackVersions", "host_name");
  protected static final String HOST_STACK_VERSION_STACK_PROPERTY_ID           = PropertyHelper.getPropertyId("HostStackVersions", "stack");
  protected static final String HOST_STACK_VERSION_VERSION_PROPERTY_ID         = PropertyHelper.getPropertyId("HostStackVersions", "version");
  protected static final String HOST_STACK_VERSION_STATE_PROPERTY_ID           = PropertyHelper.getPropertyId("HostStackVersions", "state");
  protected static final String HOST_STACK_VERSION_REPOSITORIES_PROPERTY_ID    = PropertyHelper.getPropertyId("HostStackVersions", "repositories");
  protected static final String HOST_STACK_VERSION_REPO_VERSION_PROPERTY_ID    = PropertyHelper.getPropertyId("HostStackVersions", "repository_version");

  /**
   * Whether to force creating of install command on a host which is not member of any cluster yet.
   * This will also run hdp-select for specified stack version.
   */
  protected static final String HOST_STACK_VERSION_FORCE_INSTALL_ON_NON_MEMBER_HOST_PROPERTY_ID = PropertyHelper
    .getPropertyId("HostStackVersions", "force_non_member_install");

  /**
   * In case of force_non_member_install = true a list of component names must be provided in the request.
   */
  protected static final String HOST_STACK_VERSION_COMPONENT_NAMES_PROPERTY_ID = PropertyHelper.getPropertyId("HostStackVersions", "components");
  protected static final String COMPONENT_NAME_PROPERTY_ID = "name";

  protected static final String INSTALL_PACKAGES_ACTION = "install_packages";
  protected static final String STACK_SELECT_ACTION = "stack_select_set_all";
  protected static final String INSTALL_PACKAGES_FULL_NAME = "Install Version";


  private static final Set<String> pkPropertyIds = Sets.newHashSet(
          HOST_STACK_VERSION_CLUSTER_NAME_PROPERTY_ID,
          HOST_STACK_VERSION_HOST_NAME_PROPERTY_ID,
          HOST_STACK_VERSION_ID_PROPERTY_ID,
          HOST_STACK_VERSION_STACK_PROPERTY_ID,
          HOST_STACK_VERSION_VERSION_PROPERTY_ID,
          HOST_STACK_VERSION_REPO_VERSION_PROPERTY_ID);

  private static final Set<String> propertyIds = Sets.newHashSet(
          HOST_STACK_VERSION_ID_PROPERTY_ID,
          HOST_STACK_VERSION_CLUSTER_NAME_PROPERTY_ID,
          HOST_STACK_VERSION_HOST_NAME_PROPERTY_ID,
          HOST_STACK_VERSION_STACK_PROPERTY_ID,
          HOST_STACK_VERSION_VERSION_PROPERTY_ID,
          HOST_STACK_VERSION_STATE_PROPERTY_ID,
          HOST_STACK_VERSION_REPOSITORIES_PROPERTY_ID,
          HOST_STACK_VERSION_REPO_VERSION_PROPERTY_ID,
          HOST_STACK_VERSION_FORCE_INSTALL_ON_NON_MEMBER_HOST_PROPERTY_ID,
          HOST_STACK_VERSION_COMPONENT_NAMES_PROPERTY_ID);

  private static final Map<Type, String> keyPropertyIds = new HashMap<Type, String>() {
    {
      put(Type.Cluster, HOST_STACK_VERSION_CLUSTER_NAME_PROPERTY_ID);
      put(Type.Host, HOST_STACK_VERSION_HOST_NAME_PROPERTY_ID);
      put(Type.HostStackVersion, HOST_STACK_VERSION_ID_PROPERTY_ID);
      put(Type.Stack, HOST_STACK_VERSION_STACK_PROPERTY_ID);
      put(Type.StackVersion, HOST_STACK_VERSION_VERSION_PROPERTY_ID);
      put(Type.RepositoryVersion, HOST_STACK_VERSION_REPO_VERSION_PROPERTY_ID);
    }
  };

  @Inject
  private static HostVersionDAO hostVersionDAO;

  @Inject
  private static RepositoryVersionDAO repositoryVersionDAO;

  @Inject
  private static StageFactory stageFactory;

  @Inject
  private static RequestFactory requestFactory;

  @Inject
  private static Provider<AmbariActionExecutionHelper> actionExecutionHelper;

  @Inject
  private static Configuration configuration;

  @Inject
  private static RepositoryVersionHelper repoVersionHelper;

  /**
   * Constructor.
   */
  public HostStackVersionResourceProvider(
          AmbariManagementController managementController) {
    super(Type.HostStackVersion, propertyIds, keyPropertyIds, managementController);
  }

  @Override
  public Set<Resource> getResources(Request request, Predicate predicate) throws
      SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException {
    final Set<Resource> resources = new HashSet<>();
    final Set<String> requestedIds = getRequestPropertyIds(request, predicate);
    final Set<Map<String, Object>> propertyMaps = getPropertyMaps(predicate);

    for (Map<String, Object> propertyMap: propertyMaps) {
      final String hostName = propertyMap.get(HOST_STACK_VERSION_HOST_NAME_PROPERTY_ID).toString();
      String clusterName = null;
      if (propertyMap.containsKey(HOST_STACK_VERSION_CLUSTER_NAME_PROPERTY_ID)) {
        clusterName = propertyMap.get(HOST_STACK_VERSION_CLUSTER_NAME_PROPERTY_ID).toString();
      }
      final Long id;
      List<HostVersionEntity> requestedEntities;
      if (propertyMap.get(HOST_STACK_VERSION_ID_PROPERTY_ID) == null) {
        if (clusterName == null) {
          requestedEntities = hostVersionDAO.findByHost(hostName);
        } else {
          requestedEntities = hostVersionDAO.findByClusterAndHost(clusterName, hostName);
        }
      } else {
        try {
          id = Long.parseLong(propertyMap.get(HOST_STACK_VERSION_ID_PROPERTY_ID).toString());
        } catch (Exception ex) {
          throw new SystemException("Stack version should have numerical id");
        }
        final HostVersionEntity entity = hostVersionDAO.findByPK(id);
        if (entity == null) {
          throw new NoSuchResourceException("There is no stack version with id " + id);
        } else {
          requestedEntities = Collections.singletonList(entity);
        }
      }
      if (requestedEntities != null) {
        addRequestedEntities(resources, requestedEntities, requestedIds, clusterName);
      }
    }

    return resources;
  }


  /**
   * Adds requested entities to resources
   * @param resources a list of resources to add to
   * @param requestedEntities requested entities
   * @param requestedIds
   * @param clusterName name of cluster or null if no any
   */
  public void addRequestedEntities(Set<Resource> resources,
                                   List<HostVersionEntity> requestedEntities,
                                   Set<String> requestedIds,
                                   String clusterName) {
    for (HostVersionEntity entity: requestedEntities) {
      StackId stackId = new StackId(entity.getRepositoryVersion().getStack());

      RepositoryVersionEntity repoVerEntity = repositoryVersionDAO.findByStackAndVersion(
          stackId, entity.getRepositoryVersion().getVersion());

      final Resource resource = new ResourceImpl(Resource.Type.HostStackVersion);

      setResourceProperty(resource, HOST_STACK_VERSION_HOST_NAME_PROPERTY_ID, entity.getHostName(), requestedIds);
      setResourceProperty(resource, HOST_STACK_VERSION_ID_PROPERTY_ID, entity.getId(), requestedIds);
      setResourceProperty(resource, HOST_STACK_VERSION_STACK_PROPERTY_ID, stackId.getStackName(), requestedIds);
      setResourceProperty(resource, HOST_STACK_VERSION_VERSION_PROPERTY_ID, stackId.getStackVersion(), requestedIds);
      setResourceProperty(resource, HOST_STACK_VERSION_STATE_PROPERTY_ID, entity.getState().name(), requestedIds);

      if (clusterName != null) {
        setResourceProperty(resource, HOST_STACK_VERSION_CLUSTER_NAME_PROPERTY_ID, clusterName, requestedIds);
      }

      if (repoVerEntity != null) {
        Long repoVersionId = repoVerEntity.getId();
        setResourceProperty(resource, HOST_STACK_VERSION_REPO_VERSION_PROPERTY_ID, repoVersionId, requestedIds);
      }

      resources.add(resource);
    }
  }


  @Override
  public RequestStatus createResources(Request request) throws SystemException,
          UnsupportedPropertyException, ResourceAlreadyExistsException,
          NoSuchParentResourceException {
    Iterator<Map<String,Object>> iterator = request.getProperties().iterator();
    String hostName;
    final String desiredRepoVersion;
    String stackName;
    String stackVersion;
    if (request.getProperties().size() > 1) {
      throw new UnsupportedOperationException("Multiple requests cannot be executed at the same time.");
    }

    Map<String, Object> propertyMap  = iterator.next();

    Set<String> requiredProperties = Sets.newHashSet(
            HOST_STACK_VERSION_HOST_NAME_PROPERTY_ID,
            HOST_STACK_VERSION_REPO_VERSION_PROPERTY_ID,
            HOST_STACK_VERSION_STACK_PROPERTY_ID,
            HOST_STACK_VERSION_VERSION_PROPERTY_ID);

    for (String requiredProperty : requiredProperties) {
      Validate.isTrue(propertyMap.containsKey(requiredProperty),
              String.format("The required property %s is not defined", requiredProperty));
    }

    String clName = (String) propertyMap.get (HOST_STACK_VERSION_CLUSTER_NAME_PROPERTY_ID);
    hostName = (String) propertyMap.get(HOST_STACK_VERSION_HOST_NAME_PROPERTY_ID);
    desiredRepoVersion = (String) propertyMap.get(HOST_STACK_VERSION_REPO_VERSION_PROPERTY_ID);
    stackName = (String) propertyMap.get(HOST_STACK_VERSION_STACK_PROPERTY_ID);
    stackVersion = (String) propertyMap.get(HOST_STACK_VERSION_VERSION_PROPERTY_ID);

    boolean forceInstallOnNonMemberHost = false;
    Set<Map<String, String>> componentNames = null;
    String forceInstallOnNonMemberHostString = (String) propertyMap.get
      (HOST_STACK_VERSION_FORCE_INSTALL_ON_NON_MEMBER_HOST_PROPERTY_ID);

    if (BooleanUtils.toBoolean(forceInstallOnNonMemberHostString)) {
      forceInstallOnNonMemberHost = true;
      componentNames = (Set<Map<String, String>>) propertyMap.get(HOST_STACK_VERSION_COMPONENT_NAMES_PROPERTY_ID);
      if (componentNames == null) {
        throw new IllegalArgumentException("In case " + HOST_STACK_VERSION_FORCE_INSTALL_ON_NON_MEMBER_HOST_PROPERTY_ID + " is set to true, the list of " +
          "components should be specified in request.");
      }
    }

    RequestStageContainer req = createInstallPackagesRequest(hostName, desiredRepoVersion, stackName, stackVersion,
      clName, forceInstallOnNonMemberHost, componentNames);
    return getRequestStatus(req.getRequestStatusResponse());
  }

  private RequestStageContainer createInstallPackagesRequest(String hostName, final String desiredRepoVersion,
                                                             String stackName, String stackVersion, String clName,
                                                             final boolean forceInstallOnNonMemberHost,
                                                             Set<Map<String, String>> componentNames)
    throws NoSuchParentResourceException, SystemException {

    Host host;
    try {
      host = getManagementController().getClusters().getHost(hostName);
    } catch (AmbariException e) {
      throw new NoSuchParentResourceException(
              String.format("Can not find host %s", hostName), e);
    }
    AmbariManagementController managementController = getManagementController();
    AmbariMetaInfo ami = managementController.getAmbariMetaInfo();

    final StackId stackId = new StackId(stackName, stackVersion);
    if (!ami.isSupportedStack(stackName, stackVersion)) {
      throw new NoSuchParentResourceException(String.format("Stack %s is not supported",
              stackId));
    }

    Set<Cluster> clusterSet;
    if (clName == null) {
      try {
        clusterSet = getManagementController().getClusters().getClustersForHost(hostName);
      } catch (AmbariException e) {
        throw new NoSuchParentResourceException(String.format((
                "Host %s does not belong to any cluster"
        ), hostName), e);
      }
    } else {
      Cluster cluster;
      try {
        cluster = getManagementController().getClusters().getCluster(clName);
      } catch (AmbariException e) {
        throw new NoSuchParentResourceException(String.format((
                "Cluster %s does not exist"
        ), clName), e);
      }
      clusterSet = Collections.singleton(cluster);
    }

    Cluster cluster;
    if (clusterSet.isEmpty()) {
      throw new UnsupportedOperationException(String.format("Host %s belongs " +
        "to 0 clusters with stack id %s. Performing %s action failed.",
        hostName, stackId, INSTALL_PACKAGES_FULL_NAME));
    } else if (clusterSet.size() > 1) {
      throw new UnsupportedOperationException(String.format("Host %s belongs " +
        "to %d clusters with stack id %s. Performing %s action on multiple " +
        "clusters is not supported", hostName, clusterSet.size(), stackId,
        INSTALL_PACKAGES_FULL_NAME));
    } else {
      cluster = clusterSet.iterator().next();
    }

    RepositoryVersionEntity repoVersionEnt = repositoryVersionDAO.findByStackAndVersion(stackId, desiredRepoVersion);
    if (repoVersionEnt==null) {
      throw new IllegalArgumentException(String.format(
              "Repo version %s is not available for stack %s",
              desiredRepoVersion, stackId));
    }

    HostVersionEntity hostVersEntity = hostVersionDAO.findByClusterStackVersionAndHost(clName, stackId,
            desiredRepoVersion, hostName);
    if (!forceInstallOnNonMemberHost) {
      if (hostVersEntity == null) {
        throw new IllegalArgumentException(String.format(
          "Repo version %s for stack %s is not available for host %s",
          desiredRepoVersion, stackId, hostName));
      }
      if (hostVersEntity.getState() != RepositoryVersionState.INSTALLED &&
        hostVersEntity.getState() != RepositoryVersionState.INSTALL_FAILED &&
        hostVersEntity.getState() != RepositoryVersionState.OUT_OF_SYNC) {
        throw new UnsupportedOperationException(String.format("Repo version %s for stack %s " +
            "for host %s is in %s state. Can not transition to INSTALLING state",
          desiredRepoVersion, stackId, hostName, hostVersEntity.getState().toString()));
      }
    }

    // Determine repositories for host
    String osFamily = host.getOsFamily();
    RepoOsEntity osEntity = null;
    for (RepoOsEntity operatingSystem : repoVersionEnt.getRepoOsEntities()) {
      if (osFamily.equals(operatingSystem.getFamily())) {
        osEntity = operatingSystem;
        break;
      }
    }

    if (null == osEntity) {
      throw new SystemException(String.format("Operating System matching %s could not be found",
          osFamily));
    }

    if (CollectionUtils.isEmpty(osEntity.getRepoDefinitionEntities())) {
      throw new SystemException(String.format("Repositories for os type %s are " +
                      "not defined. Repo version=%s, stackId=%s",
        osFamily, desiredRepoVersion, stackId));
    }

    Set<String> servicesOnHost = new HashSet<>();

    if (forceInstallOnNonMemberHost) {
      for (Map<String, String> componentProperties : componentNames) {

        String componentName = componentProperties.get(COMPONENT_NAME_PROPERTY_ID);
        if (StringUtils.isEmpty(componentName)) {
          throw new IllegalArgumentException("Components list contains a component with no 'name' property");
        }

        String serviceName = null;
        try {
          serviceName = ami.getComponentToService(stackName, stackVersion, componentName.trim().toUpperCase());
          if (serviceName == null) {
            throw new IllegalArgumentException("Service not found for component : " + componentName);
          }
          servicesOnHost.add(serviceName);
        } catch (AmbariException e) {
          LOG.error("Service not found for component {}!", componentName, e);
          throw new IllegalArgumentException("Service not found for component : " + componentName);
        }
      }

    } else {
      List<ServiceComponentHost> components = cluster.getServiceComponentHosts(host.getHostName());
      for (ServiceComponentHost component : components) {
        servicesOnHost.add(component.getServiceName());
      }
    }

    Map<String, String> roleParams = repoVersionHelper.buildRoleParams(managementController, repoVersionEnt,
        osFamily, servicesOnHost);

    // Create custom action
    RequestResourceFilter filter = new RequestResourceFilter(null, null,
            Collections.singletonList(hostName));

    roleParams.put(OVERRIDE_CONFIGS, null);
    roleParams.put(OVERRIDE_STACK_NAME, null);
    ActionExecutionContext actionContext = new ActionExecutionContext(
            null, INSTALL_PACKAGES_ACTION,
            Collections.singletonList(filter),
            roleParams);
    actionContext.setTimeout(Integer.valueOf(configuration.getDefaultAgentTaskTimeout(true)));
    actionContext.setStackId(repoVersionEnt.getStackId());

    repoVersionHelper.addCommandRepositoryToContext(actionContext, repoVersionEnt, osEntity);

    String caption = String.format(INSTALL_PACKAGES_FULL_NAME + " on host %s", hostName);
    RequestStageContainer req = createRequest(caption);

    Map<String, String> hostLevelParams = new HashMap<>();
    hostLevelParams.put(JDK_LOCATION, getManagementController().getJdkResourceUrl());

    // Generate cluster host info
    String clusterHostInfoJson;
    try {
      clusterHostInfoJson = StageUtils.getGson().toJson(
        StageUtils.getClusterHostInfo(cluster));
    } catch (AmbariException e) {
      throw new SystemException("Could not build cluster topology", e);
    }

    Stage stage = stageFactory.createNew(req.getId(),
            "/tmp/ambari",
            cluster.getClusterName(),
            cluster.getClusterId(),
            caption,
            "{}",
            StageUtils.getGson().toJson(hostLevelParams));

    long stageId = req.getLastStageId() + 1;
    if (0L == stageId) {
      stageId = 1L;
    }
    stage.setStageId(stageId);
    req.addStages(Collections.singletonList(stage));

    try {
      actionExecutionHelper.get().addExecutionCommandsToStage(actionContext, stage, null, !forceInstallOnNonMemberHost);
    } catch (AmbariException e) {
      throw new SystemException("Can not modify stage", e);
    }

    if (forceInstallOnNonMemberHost) {
      addSelectStackStage(desiredRepoVersion, forceInstallOnNonMemberHost, cluster, filter, caption, req,
        hostLevelParams, clusterHostInfoJson);
    }

    try {
      if (!forceInstallOnNonMemberHost) {
        hostVersEntity.setState(RepositoryVersionState.INSTALLING);
        hostVersionDAO.merge(hostVersEntity);
      }
      req.persist();
    } catch (AmbariException e) {
      throw new SystemException("Can not persist request", e);
    }
    return req;
  }

  private void addSelectStackStage(String desiredRepoVersion, boolean forceInstallOnNonMemberHost, Cluster cluster,
                                 RequestResourceFilter filter, String caption, RequestStageContainer req, Map<String, String> hostLevelParams, String clusterHostInfoJson) throws SystemException {
    Stage stage;
    long stageId;
    ActionExecutionContext actionContext;
    Map<String, String> commandParams = new HashMap<>();
    commandParams.put("version", desiredRepoVersion);

    stage = stageFactory.createNew(req.getId(),
      "/tmp/ambari",
      cluster.getClusterName(),
      cluster.getClusterId(),
      caption,
      StageUtils.getGson().toJson(commandParams),
      StageUtils.getGson().toJson(hostLevelParams));

    stageId = req.getLastStageId() + 1;
    if (0L == stageId) {
      stageId = 1L;
    }
    stage.setStageId(stageId);
    req.addStages(Collections.singletonList(stage));

    actionContext = new ActionExecutionContext(
      null, STACK_SELECT_ACTION,
      Collections.singletonList(filter),
      Collections.emptyMap());
    actionContext.setTimeout(Integer.valueOf(configuration.getDefaultAgentTaskTimeout(true)));

    try {
      actionExecutionHelper.get().addExecutionCommandsToStage(actionContext, stage, null, !forceInstallOnNonMemberHost);
    } catch (AmbariException e) {
      throw new SystemException("Can not modify stage", e);
    }
  }


  private RequestStageContainer createRequest(String caption) {
    ActionManager actionManager = getManagementController().getActionManager();

    RequestStageContainer requestStages = new RequestStageContainer(
            actionManager.getNextRequestId(), null, requestFactory, actionManager);
    requestStages.setRequestContext(caption);

    return requestStages;
  }

  @Override
  public RequestStatus updateResources(Request request, Predicate predicate)
      throws SystemException, UnsupportedPropertyException,
      NoSuchResourceException, NoSuchParentResourceException {
    throw new SystemException("Method not supported");
  }

  @Override
  public RequestStatus deleteResources(Request request, Predicate predicate)
      throws SystemException, UnsupportedPropertyException,
      NoSuchResourceException, NoSuchParentResourceException {
    throw new SystemException("Method not supported");
  }

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