class ExecutionCommandWrapper {

  public ExecutionCommand getExecutionCommand() {
    if (executionCommand != null) {
      return executionCommand;
    }

    if( null == jsonExecutionCommand ){
      throw new RuntimeException(
          "Invalid ExecutionCommandWrapper, both object and string representations are null");
    }

    try {
      executionCommand = gson.fromJson(jsonExecutionCommand, ExecutionCommand.class);

      // sanity; if no configurations, just initialize to prevent NPEs
      if (null == executionCommand.getConfigurations()) {
        executionCommand.setConfigurations(new TreeMap<>());
      }

      Map<String, Map<String, String>> configurations = executionCommand.getConfigurations();

      // For a configuration type, both tag and an actual configuration can be stored
      // Configurations from the tag is always expanded and then over-written by the actual
      // global:version1:{a1:A1,b1:B1,d1:D1} + global:{a1:A2,c1:C1,DELETED_d1:x} ==>
      // global:{a1:A2,b1:B1,c1:C1}
      Long clusterId = hostRoleCommandDAO.findByPK(
          executionCommand.getTaskId()).getStage().getClusterId();

      Cluster cluster = clusters.getClusterById(clusterId);

      // Execution commands may have config-tags already set during their creation.
      // However, these tags become stale at runtime when other
      // ExecutionCommands run and change the desired configs (like
      // ConfigureAction). Hence an ExecutionCommand can specify which
      // config-types should be refreshed at runtime. Specifying <code>*</code>
      // will result in all config-type tags to be refreshed to the latest
      // cluster desired-configs. Additionally, there may be no configuration
      // tags set but refresh might be set to *. In this case, they should still
      // be refreshed with the latest.
      boolean refreshConfigTagsBeforeExecution = executionCommand.getForceRefreshConfigTagsBeforeExecution();
      if (refreshConfigTagsBeforeExecution) {
        Map<String, DesiredConfig> desiredConfigs = cluster.getDesiredConfigs();

        Map<String, Map<String, String>> configurationTags = configHelper.getEffectiveDesiredTags(
            cluster, executionCommand.getHostname(), desiredConfigs);

        LOG.debug(
            "While scheduling task {} on cluster {}, configurations are being refreshed using desired configurations of {}",
            executionCommand.getTaskId(), cluster.getClusterName(), desiredConfigs);

        // then clear out any existing configurations so that all of the new
        // configurations are forcefully applied
        configurations.clear();
        executionCommand.setConfigurationTags(configurationTags);
      }

      // now that the tags have been updated (if necessary), fetch the
      // configurations
      Map<String, Map<String, String>> configurationTags = executionCommand.getConfigurationTags();
      configHelper.getAndMergeHostConfigs(configurations, configurationTags, cluster);
      configHelper.getAndMergeHostConfigAttributes(executionCommand.getConfigurationAttributes(),
          configurationTags, cluster);

      setVersions(cluster);

      // provide some basic information about a cluster upgrade if there is one
      // in progress
      UpgradeEntity upgrade = cluster.getUpgradeInProgress();
      if (null != upgrade) {
        UpgradeContext upgradeContext = upgradeContextFactory.create(cluster, upgrade);
        UpgradeSummary upgradeSummary = upgradeContext.getUpgradeSummary();

        executionCommand.setUpgradeSummary(upgradeSummary);
      }

      // setting repositoryFile
      final Host host = cluster.getHost(executionCommand.getHostname());  // can be null on internal commands
      final String serviceName = executionCommand.getServiceName(); // can be null on executing special RU tasks

      if (null == executionCommand.getRepositoryFile() && null != host && null != serviceName) {
        final CommandRepository commandRepository;
        final Service service = cluster.getService(serviceName);
        final String componentName = executionCommand.getComponentName();

        try {
          if (null != componentName) {
            ServiceComponent serviceComponent = service.getServiceComponent(componentName);
            commandRepository = repoVersionHelper.getCommandRepository(cluster, serviceComponent, host);
          } else {
            RepositoryVersionEntity repoVersion = service.getDesiredRepositoryVersion();
            RepoOsEntity osEntity = repoVersionHelper.getOSEntityForHost(host, repoVersion);
            commandRepository = repoVersionHelper.getCommandRepository(repoVersion, osEntity);
          }
          executionCommand.setRepositoryFile(commandRepository);

        } catch (SystemException e) {
          LOG.debug("Unable to find command repository with a correct operating system for host {}",
              host, e);
        }
      }

    } catch (ClusterNotFoundException cnfe) {
      // it's possible that there are commands without clusters; in such cases,
      // just return the de-serialized command and don't try to read configs
      LOG.warn(
          "Unable to lookup the cluster by ID; assuming that there is no cluster and therefore no configs for this execution command: {}",
          cnfe.getMessage());

      return executionCommand;
    } catch (AmbariException e) {
      throw new RuntimeException(e);
    }

    return executionCommand;
  }

  @AssistedInject public  ExecutionCommandWrapper(@Assisted String jsonExecutionCommand);
  @AssistedInject public  ExecutionCommandWrapper(@Assisted ExecutionCommand executionCommand);

}