/**
   * finds the entity indicated by the given ID or name
   *
   * <p>prefers ID based lookup in which case appId is optional, and if supplied will be enforced.
   * optionally the name can be supplied, for cases when paths should work across versions, in which
   * case names will be searched recursively (and the application is required).
   *
   * @throws 404 or 412 (unless input is null in which case output is null)
   */
  public EntityLocal getEntity(String application, String entity) {
    if (entity == null) return null;
    Application app = application != null ? getApplication(application) : null;
    EntityLocal e = (EntityLocal) mgmt.getEntityManager().getEntity(entity);

    if (e != null) {
      if (!Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_ENTITY, e)) {
        throw WebResourceUtils.notFound(
            "Cannot find entity '%s': no known ID and application not supplied for searching",
            entity);
      }

      if (app == null || app.equals(findTopLevelApplication(e))) return e;
      throw WebResourceUtils.preconditionFailed(
          "Application '%s' specified does not match application '%s' to which entity '%s' (%s) is associated",
          application, e.getApplication().getId(), entity, e);
    }
    if (application == null)
      throw WebResourceUtils.notFound(
          "Cannot find entity '%s': no known ID and application not supplied for searching",
          entity);

    assert app != null : "null app should not be returned from getApplication";
    e = searchForEntityNamed(app, entity);
    if (e != null) return e;
    throw WebResourceUtils.notFound(
        "Cannot find entity '%s' in application '%s' (%s)", entity, application, app);
  }
 public Task<?> expunge(final Entity entity, final boolean release) {
   if (mgmt.getEntitlementManager()
       .isEntitled(
           Entitlements.getEntitlementContext(),
           Entitlements.INVOKE_EFFECTOR,
           Entitlements.EntityAndItem.of(entity, "expunge"))) {
     return mgmt.getExecutionManager()
         .submit(
             MutableMap.of(
                 "displayName",
                 "expunging " + entity,
                 "description",
                 "REST call to expunge entity " + entity.getDisplayName() + " (" + entity + ")"),
             new Runnable() {
               @Override
               public void run() {
                 if (release) Entities.destroyCatching(entity);
                 else mgmt.getEntityManager().unmanage(entity);
               }
             });
   }
   throw WebResourceUtils.unauthorized(
       "User '%s' is not authorized to expunge entity %s",
       Entitlements.getEntitlementContext().user(), entity);
 }
  /**
   * true if the URL points to content which must be resolved on the server-side (i.e. classpath)
   * and which is safe to do so (currently just images, though in future perhaps also javascript and
   * html plugins)
   *
   * <p>note we do not let caller access classpath through this mechanism, just those which are
   * supplied by the platform administrator e.g. as an icon url
   */
  public boolean isUrlServerSideAndSafe(String url) {
    if (Strings.isEmpty(url)) return false;
    String ext = Files.getFileExtension(url);
    if (Strings.isEmpty(ext)) return false;
    MediaType mime = WebResourceUtils.getImageMediaTypeFromExtension(ext);
    if (mime == null) return false;

    return !Urls.isUrlWithProtocol(url) || url.startsWith("classpath:");
  }
  /**
   * finds the policy indicated by the given ID or name.
   *
   * @see {@link getPolicy(String,String,String)}.
   *     <p>
   * @throws 404 or 412 (unless input is null in which case output is null)
   */
  public Policy getPolicy(Entity entity, String policy) {
    if (policy == null) return null;

    for (Policy p : entity.getPolicies()) {
      if (policy.equals(p.getId())) return p;
    }
    for (Policy p : entity.getPolicies()) {
      if (policy.equals(p.getName())) return p;
    }

    throw WebResourceUtils.notFound("Cannot find policy '%s' in entity '%s'", policy, entity);
  }
  @SuppressWarnings({"rawtypes"})
  public Response createCatalogEntryFromGroovyCode(String groovyCode) {
    ClassLoader parent = getCatalog().getRootClassLoader();
    GroovyClassLoader loader = new GroovyClassLoader(parent);

    Class clazz = loader.parseClass(groovyCode);

    if (AbstractEntity.class.isAssignableFrom(clazz)) {
      CatalogItem<?> item = getCatalog().addItem(clazz);
      log.info("REST created " + item);
      return Response.created(URI.create("entities/" + clazz.getName())).build();

    } else if (AbstractPolicy.class.isAssignableFrom(clazz)) {
      CatalogItem<?> item = getCatalog().addItem(clazz);
      log.info("REST created " + item);
      return Response.created(URI.create("policies/" + clazz.getName())).build();
    }

    throw WebResourceUtils.preconditionFailed(
        "Unsupported type superclass " + clazz.getSuperclass() + "; expects Entity or Policy");
  }
  @SuppressWarnings("unchecked")
  public Application create(ApplicationSpec spec) {
    log.debug("REST creating application instance for {}", spec);

    if (!Entitlements.isEntitled(
        mgmt.getEntitlementManager(), Entitlements.DEPLOY_APPLICATION, spec)) {
      throw WebResourceUtils.unauthorized(
          "User '%s' is not authorized to deploy application %s",
          Entitlements.getEntitlementContext().user(), spec);
    }

    final String type = spec.getType();
    final String name = spec.getName();
    final Map<String, String> configO = spec.getConfig();
    final Set<EntitySpec> entities =
        (spec.getEntities() == null) ? ImmutableSet.<EntitySpec>of() : spec.getEntities();

    final Application instance;

    // Load the class; first try to use the appropriate catalog item; but then allow anything that
    // is on the classpath
    final Class<? extends Entity> clazz;
    if (Strings.isEmpty(type)) {
      clazz = BasicApplication.class;
    } else {
      Class<? extends Entity> tempclazz;
      try {
        tempclazz = getCatalog().loadClassByType(type, Entity.class);
      } catch (NoSuchElementException e) {
        try {
          tempclazz = (Class<? extends Entity>) getCatalog().getRootClassLoader().loadClass(type);
          log.info(
              "Catalog does not contain item for type {}; loaded class directly instead", type);
        } catch (ClassNotFoundException e2) {
          log.warn(
              "No catalog item for type {}, and could not load class directly; rethrowing", type);
          throw e;
        }
      }
      clazz = tempclazz;
    }
    if (Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.INVOKE_EFFECTOR, null)) {

      try {
        if (ApplicationBuilder.class.isAssignableFrom(clazz)) {
          Constructor<?> constructor = clazz.getConstructor();
          ApplicationBuilder appBuilder = (ApplicationBuilder) constructor.newInstance();
          if (!Strings.isEmpty(name)) appBuilder.appDisplayName(name);
          if (entities.size() > 0)
            log.warn(
                "Cannot supply additional entities when using an ApplicationBuilder; ignoring in spec {}",
                spec);

          log.info("REST placing '{}' under management", spec.getName());
          appBuilder.configure(convertFlagsToKeys(appBuilder.getType(), configO));
          configureRenderingMetadata(spec, appBuilder);
          instance = appBuilder.manage(mgmt);

        } else if (Application.class.isAssignableFrom(clazz)) {
          brooklyn.entity.proxying.EntitySpec<?> coreSpec = toCoreEntitySpec(clazz, name, configO);
          configureRenderingMetadata(spec, coreSpec);
          instance = (Application) mgmt.getEntityManager().createEntity(coreSpec);
          for (EntitySpec entitySpec : entities) {
            log.info("REST creating instance for entity {}", entitySpec.getType());
            instance.addChild(mgmt.getEntityManager().createEntity(toCoreEntitySpec(entitySpec)));
          }

          log.info(
              "REST placing '{}' under management", spec.getName() != null ? spec.getName() : spec);
          Entities.startManagement(instance, mgmt);

        } else if (Entity.class.isAssignableFrom(clazz)) {
          if (entities.size() > 0)
            log.warn(
                "Cannot supply additional entities when using a non-application entity; ignoring in spec {}",
                spec);

          brooklyn.entity.proxying.EntitySpec<?> coreSpec =
              toCoreEntitySpec(BasicApplication.class, name, configO);
          configureRenderingMetadata(spec, coreSpec);

          instance = (Application) mgmt.getEntityManager().createEntity(coreSpec);

          final Class<? extends Entity> eclazz =
              getCatalog().loadClassByType(spec.getType(), Entity.class);
          Entity soleChild =
              mgmt.getEntityManager().createEntity(toCoreEntitySpec(eclazz, name, configO));
          instance.addChild(soleChild);
          instance.addEnricher(Enrichers.builder().propagatingAll().from(soleChild).build());

          log.info("REST placing '{}' under management", spec.getName());
          Entities.startManagement(instance, mgmt);

        } else {
          throw new IllegalArgumentException(
              "Class " + clazz + " must extend one of ApplicationBuilder, Application or Entity");
        }

        return instance;

      } catch (Exception e) {
        log.error("REST failed to create application: " + e, e);
        throw Exceptions.propagate(e);
      }
    }
    throw WebResourceUtils.unauthorized(
        "User '%s' is not authorized to create application from applicationSpec %s",
        Entitlements.getEntitlementContext().user(), spec);
  }