@Override
  public JsonNode fetch(String entityIds) {
    Map<String, JsonNode> jsonEntitiesById = MutableMap.of();
    for (Application application : mgmt().getApplications())
      jsonEntitiesById.put(application.getId(), fromEntity(application));
    if (entityIds != null) {
      for (String entityId : entityIds.split(",")) {
        Entity entity = mgmt().getEntityManager().getEntity(entityId.trim());
        while (entity != null && entity.getParent() != null) {
          if (Entitlements.isEntitled(
              mgmt().getEntitlementManager(), Entitlements.SEE_ENTITY, entity)) {
            jsonEntitiesById.put(entity.getId(), fromEntity(entity));
          }
          entity = entity.getParent();
        }
      }
    }

    ArrayNode result = mapper().createArrayNode();
    for (JsonNode n : jsonEntitiesById.values()) result.add(n);
    return result;
  }
  // TODO when applicationTree can be removed, replace this with an extension to EntitySummary
  // (without links)
  private JsonNode fromEntity(Entity entity) {
    ObjectNode aRoot = entityBase(entity);

    aRoot.put("applicationId", entity.getApplicationId());

    if (entity.getParent() != null) {
      aRoot.put("parentId", entity.getParent().getId());
    }

    if (!entity.groups().isEmpty()) aRoot.put("groupIds", entitiesIdAsArray(entity.groups()));

    if (!entity.getChildren().isEmpty())
      aRoot.put("children", entitiesIdAndNameAsArray(entity.getChildren()));

    if (entity instanceof Group) {
      // use attribute instead of method in case it is read-only
      Collection<Entity> members = entity.getAttribute(AbstractGroup.GROUP_MEMBERS);
      if (members != null && !members.isEmpty())
        aRoot.put("members", entitiesIdAndNameAsArray(members));
    }

    return aRoot;
  }