@SafeVarargs
  private static <U extends AbstractElement.Update, T extends AbstractElement<?, U>> void install(
      Inventory inventory,
      Set<Subscription> subscriptions,
      Class<T> entityClass,
      MessageSender sender,
      Action<?, T>... additionalActions) {

    installAction(inventory, subscriptions, entityClass, sender, Action.created());
    installAction(inventory, subscriptions, entityClass, sender, Action.updated());
    installAction(inventory, subscriptions, entityClass, sender, Action.deleted());
    for (Action<?, T> a : additionalActions) {
      installAction(inventory, subscriptions, entityClass, sender, a);
    }
  }
  public static InventoryEvent<?> from(Action<?, ?> action, Object object) {
    if (object == null) {
      throw new IllegalArgumentException("object == null");
    }

    if (action == null) {
      throw new IllegalArgumentException("action == null");
    }

    if (object instanceof Tenant) {
      return new TenantEvent(action.asEnum(), (Tenant) object);
    } else if (object instanceof Environment) {
      return new EnvironmentEvent(action.asEnum(), (Environment) object);
    } else if (object instanceof Feed) {
      return new FeedEvent(action.asEnum(), (Feed) object);
    } else if (object instanceof Metric) {
      return new MetricEvent(action.asEnum(), (Metric) object);
    } else if (object instanceof MetricType) {
      return new MetricTypeEvent(action.asEnum(), (MetricType) object);
    } else if (object instanceof Resource) {
      return new ResourceEvent(action.asEnum(), (Resource) object);
    } else if (object instanceof ResourceType) {
      return new ResourceTypeEvent(action.asEnum(), (ResourceType) object);
    } else if (object instanceof Relationship) {
      return new RelationshipEvent(action.asEnum(), (Relationship) object);
    } else if (object instanceof DataEntity) {
      return new DataEntityEvent(action.asEnum(), (DataEntity) object);
    } else if (object instanceof Action.Update) {
      @SuppressWarnings("unchecked")
      AbstractElement<?, AbstractElement.Update> updated =
          (AbstractElement<?, AbstractElement.Update>) ((Action.Update) object).getOriginalEntity();

      updated.update().with((AbstractElement.Update) ((Action.Update) object).getUpdate());

      // TODO should we instead send the whole update object? No time for that now, but it'd be
      // preferable I think
      return from(action, updated);
    } else {
      throw new IllegalArgumentException("Unsupported entity type: " + object.getClass());
    }
  }
 private void install() {
   install(inventory, subscriptions, Tenant.class, messageSender);
   install(
       inventory, subscriptions, ResourceType.class, messageSender, Action.identityHashChanged());
   install(
       inventory, subscriptions, MetricType.class, messageSender, Action.identityHashChanged());
   install(inventory, subscriptions, Environment.class, messageSender, Action.copied());
   install(
       inventory,
       subscriptions,
       Feed.class,
       messageSender,
       Action.registered(),
       Action.identityHashChanged());
   install(inventory, subscriptions, Resource.class, messageSender, Action.identityHashChanged());
   install(inventory, subscriptions, Metric.class, messageSender, Action.identityHashChanged());
   install(inventory, subscriptions, Relationship.class, messageSender);
   install(
       inventory, subscriptions, DataEntity.class, messageSender, Action.identityHashChanged());
 }