class HostVersionOutOfSyncListener {

  @Subscribe
  @Transactional
  public void onServiceEvent(ServiceInstalledEvent event) {
    if (LOG.isDebugEnabled()) {
      LOG.debug(event.toString());
    }

    try {
      Cluster cluster = clusters.get().getClusterById(event.getClusterId());

      Map<String, ServiceComponent> serviceComponents = cluster.getService(event.getServiceName()).getServiceComponents();
      // Determine hosts that become OUT_OF_SYNC when adding components for new service
      Map<String, List<ServiceComponent>> affectedHosts =
        new HashMap<>();
      for (ServiceComponent component : serviceComponents.values()) {
        for (String hostname : component.getServiceComponentHosts().keySet()) {
          if (! affectedHosts.containsKey(hostname)) {
            affectedHosts.put(hostname, new ArrayList<>());
          }
          affectedHosts.get(hostname).add(component);
        }
      }
      for (String hostName : affectedHosts.keySet()) {
        List<HostVersionEntity> hostVersionEntities =
            hostVersionDAO.get().findByClusterAndHost(cluster.getClusterName(), hostName);
        for (HostVersionEntity hostVersionEntity : hostVersionEntities) {
          RepositoryVersionEntity repositoryVersion = hostVersionEntity.getRepositoryVersion();
          // If added components do not advertise version, it makes no sense to mark version OUT_OF_SYNC
          // We perform check per-stack version, because component may be not versionAdvertised in current
          // stack, but become versionAdvertised in some future (installed, but not yet upgraded to) stack
          boolean hasChangedComponentsWithVersions = false;
          String serviceName = event.getServiceName();
          for (ServiceComponent comp : affectedHosts.get(hostName)) {
            String componentName = comp.getName();

            // Skip lookup if stack does not contain the component
            if (!ami.get().isValidServiceComponent(repositoryVersion.getStackName(),
                repositoryVersion.getStackVersion(), serviceName, componentName)) {
              LOG.debug("Component not found is host stack, stack={}, version={}, service={}, component={}",
                  repositoryVersion.getStackName(), repositoryVersion.getStackVersion(), serviceName, componentName);
              continue;
            }
            ComponentInfo component = ami.get().getComponent(repositoryVersion.getStackName(),
                    repositoryVersion.getStackVersion(), serviceName, componentName);
            if (component.isVersionAdvertised()) {
              hasChangedComponentsWithVersions = true;
            }
          }
          if (! hasChangedComponentsWithVersions) {
            continue;
          }

          if (hostVersionEntity.getState().equals(RepositoryVersionState.INSTALLED)) {
            hostVersionEntity.setState(RepositoryVersionState.OUT_OF_SYNC);
            hostVersionDAO.get().merge(hostVersionEntity);
          }
        }
      }

    } catch (AmbariException e) {
      LOG.error("Can not update hosts about out of sync", e);
    }
  }

  @Inject public  HostVersionOutOfSyncListener(AmbariEventPublisher ambariEventPublisher, LockFactory lockFactory);

}