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

    m_lock.lock();

    try {
      Cluster cluster = clusters.get().getClusterById(event.getClusterId());
      List<HostVersionEntity> hostVersionEntities =
          hostVersionDAO.get().findByClusterAndHost(cluster.getClusterName(), event.getHostName());

      Service service = cluster.getService(event.getServiceName());
      ServiceComponent serviceComponent = service.getServiceComponent(event.getComponentName());
      RepositoryVersionEntity componentRepo = serviceComponent.getDesiredRepositoryVersion();

      for (HostVersionEntity hostVersionEntity : hostVersionEntities) {
        StackEntity hostStackEntity = hostVersionEntity.getRepositoryVersion().getStack();
        StackId hostStackId = new StackId(hostStackEntity);

        // 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
        String serviceName = event.getServiceName();
        String componentName = event.getComponentName();

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

        if (!component.isVersionAdvertised()) {
          RepositoryVersionState state = checkAllHostComponents(hostStackId, hostVersionEntity.getHostEntity());
          if (null != state) {
            hostVersionEntity.setState(state);
            hostVersionDAO.get().merge(hostVersionEntity);
          }
          continue;
        }

        // !!! we shouldn't be changing other versions to OUT_OF_SYNC if the event
        // component repository doesn't match
        if (!hostVersionEntity.getRepositoryVersion().equals(componentRepo)) {
          continue;
        }

        switch (hostVersionEntity.getState()) {
          case INSTALLED:
          case NOT_REQUIRED:
            hostVersionEntity.setState(RepositoryVersionState.OUT_OF_SYNC);
            hostVersionDAO.get().merge(hostVersionEntity);
            break;
          default:
            break;
        }
      }
    } catch (AmbariException e) {
      LOG.error("Can not update hosts about out of sync", e);
    } finally {
      m_lock.unlock();
    }
  }
The publisher may be an asynchronous, multi-threaded one, so to avoid the (rare, but possible) case
of both an Install and Uninstall event occurring at the same time, we use a Lock.
ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/HostVersionOutOfSyncListener.java