@Override
 public <T> List<T> listByGroup(
     final AutoScalingGroupMetadata group,
     final Predicate<? super AutoScalingInstance> filter,
     final Function<? super AutoScalingInstance, T> transform)
     throws AutoScalingMetadataException {
   final AutoScalingInstance example = exampleForGroup(group);
   return persistenceSupport.listByExample(example, filter, transform);
 }
 @Override
 public <T> List<T> listUnhealthyByGroup(
     final AutoScalingGroupMetadata group,
     final Function<? super AutoScalingInstance, T> transform)
     throws AutoScalingMetadataException {
   final AutoScalingInstance example = exampleForGroup(group);
   example.setHealthStatus(HealthStatus.Unhealthy);
   return persistenceSupport.listByExample(example, Predicates.alwaysTrue(), transform);
 }
 @Override
 public <T> List<T> listByGroup(
     final OwnerFullName ownerFullName,
     final String groupName,
     final Function<? super AutoScalingInstance, T> transform)
     throws AutoScalingMetadataException {
   final AutoScalingInstance example = AutoScalingInstance.withOwner(ownerFullName);
   example.setAutoScalingGroupName(groupName);
   return persistenceSupport.listByExample(example, Predicates.alwaysTrue(), transform);
 }
 @Override
 public <T> List<T> listByState(
     final LifecycleState lifecycleState,
     final ConfigurationState configurationState,
     final Function<? super AutoScalingInstance, T> transform)
     throws AutoScalingMetadataException {
   final AutoScalingInstance example =
       AutoScalingInstance.withStates(lifecycleState, configurationState);
   return persistenceSupport.listByExample(
       example, Predicates.and(lifecycleState, configurationState), transform);
 }
  @Override
  public void markExpiredPendingUnhealthy(
      final AutoScalingGroupMetadata group, final Collection<String> instanceIds, final long maxAge)
      throws AutoScalingMetadataException {
    final AutoScalingInstance example = exampleForGroup(group);
    example.setHealthStatus(HealthStatus.Healthy);

    final List<AutoScalingInstance> instancesToMark =
        instanceIds.isEmpty()
            ? Collections.<AutoScalingInstance>emptyList()
            : persistenceSupport.listByExample(
                example,
                LifecycleState.Pending,
                Property.forName("displayName").in(instanceIds),
                Collections.<String, String>emptyMap(),
                Functions.<AutoScalingInstance>identity());

    for (final AutoScalingInstance instance : instancesToMark) {
      try {
        persistenceSupport.updateByExample(
            AutoScalingInstance.withUuid(instance.getNaturalId()),
            group.getOwner(),
            instance.getInstanceId(),
            new Callback<AutoScalingInstance>() {
              @Override
              public void fire(final AutoScalingInstance instance) {
                if (instance.getCreationTimestamp().getTime() < maxAge) {
                  logger.info("Marking pending instance unhealthy: " + instance.getInstanceId());
                  instance.setHealthStatus(HealthStatus.Unhealthy);
                } else {
                  logger.debug(
                      "Not marking pending instance unhealthy (within timeout): "
                          + instance.getInstanceId());
                }
              }
            });
      } catch (final AutoScalingMetadataNotFoundException e) {
        // removed, no need to mark unhealthy
      }
    }
  }
  @Override
  public void markMissingInstancesUnhealthy(
      final AutoScalingGroupMetadata group, final Collection<String> instanceIds)
      throws AutoScalingMetadataException {
    final AutoScalingInstance example = exampleForGroup(group);
    example.setHealthStatus(HealthStatus.Healthy);

    final List<AutoScalingInstance> instancesToMark =
        persistenceSupport.listByExample(
            example,
            LifecycleState.InService,
            instanceIds.isEmpty()
                ? Restrictions.conjunction()
                : Restrictions.not(Property.forName("displayName").in(instanceIds)),
            Collections.<String, String>emptyMap(),
            Functions.<AutoScalingInstance>identity());

    for (final AutoScalingInstance instance : instancesToMark) {
      try {
        persistenceSupport.updateByExample(
            AutoScalingInstance.withUuid(instance.getNaturalId()),
            group.getOwner(),
            instance.getInstanceId(),
            new Callback<AutoScalingInstance>() {
              @Override
              public void fire(final AutoScalingInstance instance) {
                if (instance.healthStatusGracePeriodExpired()) {
                  logger.info("Marking instance unhealthy: " + instance.getInstanceId());
                  instance.setHealthStatus(HealthStatus.Unhealthy);
                } else {
                  logger.debug(
                      "Instance not healthy but within grace period: " + instance.getInstanceId());
                }
              }
            });
      } catch (final AutoScalingMetadataNotFoundException e) {
        // removed, no need to mark unhealthy
      }
    }
  }
  @Override
  public Set<String> verifyInstanceIds(
      final String accountNumber, final Collection<String> instanceIds)
      throws AutoScalingMetadataException {
    final Set<String> verifiedInstanceIds = Sets.newHashSet();

    if (!instanceIds.isEmpty()) {
      final AutoScalingInstance example = AutoScalingInstance.withOwner(accountNumber);
      final Criterion idCriterion = Property.forName("displayName").in(instanceIds);

      Iterables.addAll(
          verifiedInstanceIds,
          persistenceSupport.listByExample(
              example,
              Predicates.alwaysTrue(),
              idCriterion,
              Collections.<String, String>emptyMap(),
              AutoScalingInstances.instanceId()));
    }

    return verifiedInstanceIds;
  }