You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by ha...@apache.org on 2015/08/09 04:55:19 UTC
[12/28] incubator-brooklyn git commit: brooklyn-rest-server: add
org.apache package prefix
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java
new file mode 100644
index 0000000..962d6cf
--- /dev/null
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java
@@ -0,0 +1,463 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.rest.resources;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static javax.ws.rs.core.Response.created;
+import static javax.ws.rs.core.Response.status;
+import static javax.ws.rs.core.Response.Status.ACCEPTED;
+
+import brooklyn.entity.basic.EntityPredicates;
+import brooklyn.util.text.Strings;
+import io.brooklyn.camp.spi.AssemblyTemplate;
+
+import java.io.StringReader;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
+import javax.ws.rs.core.UriInfo;
+
+import org.codehaus.jackson.JsonNode;
+import org.codehaus.jackson.node.ArrayNode;
+import org.codehaus.jackson.node.ObjectNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.catalog.internal.CatalogUtils;
+import brooklyn.entity.Application;
+import brooklyn.entity.Entity;
+import brooklyn.entity.Group;
+import brooklyn.entity.basic.AbstractGroup;
+import brooklyn.entity.basic.Attributes;
+import brooklyn.entity.basic.Lifecycle;
+import brooklyn.entity.trait.Startable;
+import brooklyn.event.AttributeSensor;
+import brooklyn.event.Sensor;
+import brooklyn.event.basic.Sensors;
+import brooklyn.location.Location;
+import brooklyn.management.Task;
+import brooklyn.management.entitlement.EntitlementPredicates;
+import brooklyn.management.entitlement.Entitlements;
+import brooklyn.management.entitlement.Entitlements.EntityAndItem;
+import brooklyn.management.entitlement.Entitlements.StringAndArgument;
+import brooklyn.management.internal.EntityManagementUtils;
+import brooklyn.management.internal.EntityManagementUtils.CreationResult;
+import brooklyn.rest.api.ApplicationApi;
+import brooklyn.rest.domain.ApplicationSpec;
+import brooklyn.rest.domain.ApplicationSummary;
+import brooklyn.rest.domain.EntitySpec;
+import brooklyn.rest.domain.EntitySummary;
+import brooklyn.rest.domain.TaskSummary;
+import org.apache.brooklyn.rest.filter.HaHotStateRequired;
+import org.apache.brooklyn.rest.transform.ApplicationTransformer;
+import org.apache.brooklyn.rest.transform.EntityTransformer;
+import org.apache.brooklyn.rest.transform.TaskTransformer;
+import org.apache.brooklyn.rest.util.BrooklynRestResourceUtils;
+import org.apache.brooklyn.rest.util.WebResourceUtils;
+import brooklyn.util.ResourceUtils;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.exceptions.Exceptions;
+
+import com.google.common.base.Throwables;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.Iterables;
+
+@HaHotStateRequired
+public class ApplicationResource extends AbstractBrooklynRestResource implements ApplicationApi {
+
+ private static final Logger log = LoggerFactory.getLogger(ApplicationResource.class);
+
+ @Context
+ private UriInfo uriInfo;
+
+ /** @deprecated since 0.6.0 use {@link #fetch(String)} (with slightly different, but better semantics) */
+ @Deprecated
+ @Override
+ public JsonNode applicationTree() {
+ ArrayNode apps = mapper().createArrayNode();
+ for (Application application : mgmt().getApplications())
+ apps.add(recursiveTreeFromEntity(application));
+ return apps;
+ }
+
+ private ObjectNode entityBase(Entity entity) {
+ ObjectNode aRoot = mapper().createObjectNode();
+ aRoot.put("name", entity.getDisplayName());
+ aRoot.put("id", entity.getId());
+ aRoot.put("type", entity.getEntityType().getName());
+
+ Boolean serviceUp = entity.getAttribute(Attributes.SERVICE_UP);
+ if (serviceUp!=null) aRoot.put("serviceUp", serviceUp);
+
+ Lifecycle serviceState = entity.getAttribute(Attributes.SERVICE_STATE_ACTUAL);
+ if (serviceState!=null) aRoot.put("serviceState", serviceState.toString());
+
+ String iconUrl = entity.getIconUrl();
+ if (iconUrl!=null) {
+ if (brooklyn().isUrlServerSideAndSafe(iconUrl))
+ // route to server if it is a server-side url
+ iconUrl = EntityTransformer.entityUri(entity)+"/icon";
+ aRoot.put("iconUrl", iconUrl);
+ }
+
+ return aRoot;
+ }
+
+ private JsonNode recursiveTreeFromEntity(Entity entity) {
+ ObjectNode aRoot = entityBase(entity);
+
+ if (!entity.getChildren().isEmpty())
+ aRoot.put("children", childEntitiesRecursiveAsArray(entity));
+
+ return aRoot;
+ }
+
+ // 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.getGroups().isEmpty())
+ aRoot.put("groupIds", entitiesIdAsArray(entity.getGroups()));
+
+ 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;
+ }
+
+ private ArrayNode childEntitiesRecursiveAsArray(Entity entity) {
+ ArrayNode node = mapper().createArrayNode();
+ for (Entity e : entity.getChildren()) {
+ if (Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_ENTITY, entity)) {
+ node.add(recursiveTreeFromEntity(e));
+ }
+ }
+ return node;
+ }
+
+ private ArrayNode entitiesIdAndNameAsArray(Collection<? extends Entity> entities) {
+ ArrayNode node = mapper().createArrayNode();
+ for (Entity entity : entities) {
+ if (Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_ENTITY, entity)) {
+ ObjectNode holder = mapper().createObjectNode();
+ holder.put("id", entity.getId());
+ holder.put("name", entity.getDisplayName());
+ node.add(holder);
+ }
+ }
+ return node;
+ }
+
+ private ArrayNode entitiesIdAsArray(Collection<? extends Entity> entities) {
+ ArrayNode node = mapper().createArrayNode();
+ for (Entity entity : entities) {
+ if (Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_ENTITY, entity)) {
+ node.add(entity.getId());
+ }
+ }
+ return node;
+ }
+
+ @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;
+ }
+
+ @Override
+ public List<ApplicationSummary> list(String typeRegex) {
+ if (Strings.isBlank(typeRegex)) {
+ typeRegex = ".*";
+ }
+ return FluentIterable
+ .from(mgmt().getApplications())
+ .filter(EntitlementPredicates.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_ENTITY))
+ .filter(EntityPredicates.hasInterfaceMatching(typeRegex))
+ .transform(ApplicationTransformer.FROM_APPLICATION)
+ .toList();
+ }
+
+ @Override
+ public ApplicationSummary get(String application) {
+ return ApplicationTransformer.summaryFromApplication(brooklyn().getApplication(application));
+ }
+
+ public Response create(ApplicationSpec applicationSpec) {
+ return createFromAppSpec(applicationSpec);
+ }
+
+ /** @deprecated since 0.7.0 see #create */ @Deprecated
+ protected Response createFromAppSpec(ApplicationSpec applicationSpec) {
+ if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.DEPLOY_APPLICATION, applicationSpec)) {
+ throw WebResourceUtils.unauthorized("User '%s' is not authorized to start application %s",
+ Entitlements.getEntitlementContext().user(), applicationSpec);
+ }
+
+ checkApplicationTypesAreValid(applicationSpec);
+ checkLocationsAreValid(applicationSpec);
+ // TODO duplicate prevention
+ List<Location> locations = brooklyn().getLocations(applicationSpec);
+ Application app = brooklyn().create(applicationSpec);
+ Task<?> t = brooklyn().start(app, locations);
+ TaskSummary ts = TaskTransformer.FROM_TASK.apply(t);
+ URI ref = uriInfo.getBaseUriBuilder()
+ .path(ApplicationApi.class)
+ .path(ApplicationApi.class, "get")
+ .build(app.getApplicationId());
+ return created(ref).entity(ts).build();
+ }
+
+ @Override
+ public Response createFromYaml(String yaml) {
+ // First of all, see if it's a URL
+ URI uri;
+ try {
+ uri = new URI(yaml);
+ } catch (URISyntaxException e) {
+ // It's not a URI then...
+ uri = null;
+ }
+ if (uri != null) {
+ log.debug("Create app called with URI; retrieving contents: {}", uri);
+ yaml = ResourceUtils.create(mgmt()).getResourceAsString(uri.toString());
+ }
+
+ log.debug("Creating app from yaml:\n{}", yaml);
+ AssemblyTemplate at = camp().pdp().registerDeploymentPlan( new StringReader(yaml) );
+
+ if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.DEPLOY_APPLICATION, at)) {
+ throw WebResourceUtils.unauthorized("User '%s' is not authorized to start application %s",
+ Entitlements.getEntitlementContext().user(), yaml);
+ }
+
+ return launch(at);
+ }
+
+ private Response launch(AssemblyTemplate at) {
+ try {
+ CreationResult<? extends Application, Void> result = EntityManagementUtils.createStarting(mgmt(), at);
+
+ Application app = result.get();
+ if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(app,
+ StringAndArgument.of(Startable.START.getName(), null)))) {
+ throw WebResourceUtils.unauthorized("User '%s' is not authorized to start application %s",
+ Entitlements.getEntitlementContext().user(), at.getType());
+ }
+
+ log.info("Launched from YAML: " + at + " -> " + app + " (" + result.task() + ")");
+
+ URI ref = URI.create(app.getApplicationId());
+ ResponseBuilder response = created(ref);
+ if (result.task() != null)
+ response.entity(TaskTransformer.FROM_TASK.apply(result.task()));
+ return response.build();
+ } catch (Exception e) {
+ throw Exceptions.propagate(e);
+ }
+ }
+
+ @Override
+ public Response createPoly(byte[] inputToAutodetectType) {
+ log.debug("Creating app from autodetecting input");
+
+ boolean looksLikeLegacy = false;
+ Exception legacyFormatException = null;
+ // attempt legacy format
+ try {
+ ApplicationSpec appSpec = mapper().readValue(inputToAutodetectType, ApplicationSpec.class);
+ return createFromAppSpec(appSpec);
+ } catch (Exception e) {
+ Exceptions.propagateIfFatal(e);
+ legacyFormatException = e;
+ log.debug("Input is not legacy ApplicationSpec JSON (will try others): "+e, e);
+ }
+
+ AssemblyTemplate template = null;
+ boolean looksLikeYaml = false;
+ try {
+ template = camp().pdp().registerDeploymentPlan(new StringReader(new String(inputToAutodetectType)));
+ if (!template.getPlatformComponentTemplates().isEmpty() || !template.getApplicationComponentTemplates().isEmpty()) {
+ looksLikeYaml = true;
+ } else {
+ if (template.getCustomAttributes().containsKey("type") || template.getCustomAttributes().containsKey("entities")) {
+ looksLikeLegacy = true;
+ }
+ }
+ } catch (Exception e) {
+ Exceptions.propagateIfFatal(e);
+ log.debug("Input is not valid YAML: "+e);
+ }
+
+ // TODO not json - try ZIP, etc
+
+ if (template!=null) {
+ try {
+ return launch(template);
+ } catch (Exception e) {
+ Exceptions.propagateIfFatal(e);
+ if (looksLikeYaml)
+ throw Exceptions.propagate(e);
+ }
+ }
+
+ if ((looksLikeLegacy || !looksLikeYaml) && legacyFormatException!=null)
+ // throw the error from legacy creation if it looks like it was the legacy format
+ throw Throwables.propagate(legacyFormatException);
+
+ return Response.serverError().entity("Unsupported format; not able to autodetect.").build();
+ }
+
+ @Override
+ public Response createFromForm(String contents) {
+ log.debug("Creating app from form");
+ return createPoly(contents.getBytes());
+ }
+
+ @Override
+ public Response delete(String application) {
+ Application app = brooklyn().getApplication(application);
+ if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.INVOKE_EFFECTOR, Entitlements.EntityAndItem.of(app,
+ StringAndArgument.of(Entitlements.LifecycleEffectors.DELETE, null)))) {
+ throw WebResourceUtils.unauthorized("User '%s' is not authorized to delete application %s",
+ Entitlements.getEntitlementContext().user(), app);
+ }
+ Task<?> t = brooklyn().destroy(app);
+ TaskSummary ts = TaskTransformer.FROM_TASK.apply(t);
+ return status(ACCEPTED).entity(ts).build();
+ }
+
+ private void checkApplicationTypesAreValid(ApplicationSpec applicationSpec) {
+ String appType = applicationSpec.getType();
+ if (appType != null) {
+ checkEntityTypeIsValid(appType);
+
+ if (applicationSpec.getEntities() != null) {
+ throw WebResourceUtils.preconditionFailed("Application given explicit type '%s' must not define entities", appType);
+ }
+ return;
+ }
+
+ for (EntitySpec entitySpec : applicationSpec.getEntities()) {
+ String entityType = entitySpec.getType();
+ checkEntityTypeIsValid(checkNotNull(entityType, "entityType"));
+ }
+ }
+
+ private void checkEntityTypeIsValid(String type) {
+ if (CatalogUtils.getCatalogItemOptionalVersion(mgmt(), type) == null) {
+ try {
+ brooklyn().getCatalogClassLoader().loadClass(type);
+ } catch (ClassNotFoundException e) {
+ log.debug("Class not found for type '" + type + "'; reporting 404", e);
+ throw WebResourceUtils.notFound("Undefined type '%s'", type);
+ }
+ log.info("Entity type '{}' not defined in catalog but is on classpath; continuing", type);
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ private void checkLocationsAreValid(ApplicationSpec applicationSpec) {
+ for (String locationId : applicationSpec.getLocations()) {
+ locationId = BrooklynRestResourceUtils.fixLocation(locationId);
+ if (!brooklyn().getLocationRegistry().canMaybeResolve(locationId) && brooklyn().getLocationRegistry().getDefinedLocationById(locationId)==null) {
+ throw WebResourceUtils.notFound("Undefined location '%s'", locationId);
+ }
+ }
+ }
+
+ @Override
+ public List<EntitySummary> getDescendants(String application, String typeRegex) {
+ return EntityTransformer.entitySummaries(brooklyn().descendantsOfType(application, application, typeRegex));
+ }
+
+ @Override
+ public Map<String, Object> getDescendantsSensor(String application, String sensor, String typeRegex) {
+ Iterable<Entity> descs = brooklyn().descendantsOfType(application, application, typeRegex);
+ return getSensorMap(sensor, descs);
+ }
+
+ public static Map<String, Object> getSensorMap(String sensor, Iterable<Entity> descs) {
+ if (Iterables.isEmpty(descs))
+ return Collections.emptyMap();
+ Map<String, Object> result = MutableMap.of();
+ Iterator<Entity> di = descs.iterator();
+ Sensor<?> s = null;
+ while (di.hasNext()) {
+ Entity potentialSource = di.next();
+ s = potentialSource.getEntityType().getSensor(sensor);
+ if (s!=null) break;
+ }
+ if (s==null)
+ s = Sensors.newSensor(Object.class, sensor);
+ if (!(s instanceof AttributeSensor<?>)) {
+ log.warn("Cannot retrieve non-attribute sensor "+s+" for entities; returning empty map");
+ return result;
+ }
+ for (Entity e: descs) {
+ Object v = null;
+ try {
+ v = e.getAttribute((AttributeSensor<?>)s);
+ } catch (Exception exc) {
+ Exceptions.propagateIfFatal(exc);
+ log.warn("Error retrieving sensor "+s+" for "+e+" (ignoring): "+exc);
+ }
+ if (v!=null)
+ result.put(e.getId(), v);
+ }
+ return result;
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java
new file mode 100644
index 0000000..2367429
--- /dev/null
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java
@@ -0,0 +1,481 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.rest.resources;
+
+import java.io.InputStream;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+import javax.annotation.Nullable;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.brooklyn.catalog.CatalogItem;
+import org.apache.brooklyn.catalog.CatalogItem.CatalogItemType;
+import brooklyn.catalog.CatalogPredicates;
+import brooklyn.catalog.internal.BasicBrooklynCatalog;
+import brooklyn.catalog.internal.CatalogDto;
+import brooklyn.catalog.internal.CatalogItemComparator;
+import brooklyn.catalog.internal.CatalogUtils;
+import brooklyn.entity.Application;
+import brooklyn.entity.Entity;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.location.Location;
+import brooklyn.location.LocationSpec;
+import brooklyn.management.entitlement.Entitlements;
+import brooklyn.management.entitlement.Entitlements.StringAndArgument;
+import brooklyn.policy.Policy;
+import brooklyn.policy.PolicySpec;
+import brooklyn.rest.api.CatalogApi;
+import brooklyn.rest.domain.ApiError;
+import brooklyn.rest.domain.CatalogEntitySummary;
+import brooklyn.rest.domain.CatalogItemSummary;
+import brooklyn.rest.domain.CatalogLocationSummary;
+import brooklyn.rest.domain.CatalogPolicySummary;
+import org.apache.brooklyn.rest.filter.HaHotStateRequired;
+import org.apache.brooklyn.rest.transform.CatalogTransformer;
+import org.apache.brooklyn.rest.util.WebResourceUtils;
+import brooklyn.util.ResourceUtils;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.collections.MutableSet;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.stream.Streams;
+import brooklyn.util.text.StringPredicates;
+import brooklyn.util.text.Strings;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.io.Files;
+import com.sun.jersey.core.header.FormDataContentDisposition;
+
+@HaHotStateRequired
+public class CatalogResource extends AbstractBrooklynRestResource implements CatalogApi {
+
+ private static final Logger log = LoggerFactory.getLogger(CatalogResource.class);
+
+ @SuppressWarnings("rawtypes")
+ private final Function<CatalogItem, CatalogItemSummary> TO_CATALOG_ITEM_SUMMARY = new Function<CatalogItem, CatalogItemSummary>() {
+ @Override
+ public CatalogItemSummary apply(@Nullable CatalogItem input) {
+ return CatalogTransformer.catalogItemSummary(brooklyn(), input);
+ }
+ };
+
+ @Override
+ @Consumes(MediaType.MULTIPART_FORM_DATA)
+ public Response createFromMultipart(InputStream uploadedInputStream, FormDataContentDisposition fileDetail) {
+ return create(Streams.readFullyString(uploadedInputStream));
+ }
+
+ static Set<String> missingIcons = MutableSet.of();
+
+ @Override
+ public Response create(String yaml) {
+ if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.ADD_CATALOG_ITEM, yaml)) {
+ throw WebResourceUtils.unauthorized("User '%s' is not authorized to add catalog item",
+ Entitlements.getEntitlementContext().user());
+ }
+
+ Iterable<? extends CatalogItem<?, ?>> items;
+ try {
+ items = brooklyn().getCatalog().addItems(yaml);
+ } catch (IllegalArgumentException e) {
+ return Response.status(Status.BAD_REQUEST)
+ .type(MediaType.APPLICATION_JSON)
+ .entity(ApiError.of(e))
+ .build();
+ }
+
+ log.info("REST created catalog items: "+items);
+
+ Map<String,Object> result = MutableMap.of();
+
+ for (CatalogItem<?,?> item: items) {
+ result.put(item.getId(), CatalogTransformer.catalogItemSummary(brooklyn(), item));
+ }
+ return Response.status(Status.CREATED).entity(result).build();
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public Response resetXml(String xml, boolean ignoreErrors) {
+ if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.MODIFY_CATALOG_ITEM, null) ||
+ !Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.ADD_CATALOG_ITEM, null)) {
+ throw WebResourceUtils.unauthorized("User '%s' is not authorized to modify catalog",
+ Entitlements.getEntitlementContext().user());
+ }
+
+ ((BasicBrooklynCatalog)mgmt().getCatalog()).reset(CatalogDto.newDtoFromXmlContents(xml, "REST reset"), !ignoreErrors);
+ return Response.ok().build();
+ }
+
+ @Override
+ @Deprecated
+ public void deleteEntity(String entityId) throws Exception {
+ if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.MODIFY_CATALOG_ITEM, StringAndArgument.of(entityId, "delete"))) {
+ throw WebResourceUtils.unauthorized("User '%s' is not authorized to modify catalog",
+ Entitlements.getEntitlementContext().user());
+ }
+ try {
+ CatalogItem<?, ?> item = CatalogUtils.getCatalogItemOptionalVersion(mgmt(), entityId);
+ if (item==null) {
+ throw WebResourceUtils.notFound("Entity with id '%s' not found", entityId);
+ } else if (item.getCatalogItemType() != CatalogItemType.ENTITY && item.getCatalogItemType() != CatalogItemType.TEMPLATE) {
+ throw WebResourceUtils.preconditionFailed("Item with id '%s' not an entity", entityId);
+ }
+ brooklyn().getCatalog().deleteCatalogItem(item.getSymbolicName(), item.getVersion());
+ } catch (NoSuchElementException e) {
+ throw WebResourceUtils.notFound("Entity with id '%s' not found", entityId);
+ }
+ }
+
+ @Override
+ public void deleteApplication(String symbolicName, String version) throws Exception {
+ deleteEntity(symbolicName, version);
+ }
+
+ @Override
+ public void deleteEntity(String symbolicName, String version) throws Exception {
+ if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.MODIFY_CATALOG_ITEM, StringAndArgument.of(symbolicName+(Strings.isBlank(version) ? "" : ":"+version), "delete"))) {
+ throw WebResourceUtils.unauthorized("User '%s' is not authorized to modify catalog",
+ Entitlements.getEntitlementContext().user());
+ }
+
+ CatalogItem<?, ?> item = mgmt().getCatalog().getCatalogItem(symbolicName, version);
+ if (item == null) {
+ throw WebResourceUtils.notFound("Entity with id '%s:%s' not found", symbolicName, version);
+ } else if (item.getCatalogItemType() != CatalogItemType.ENTITY && item.getCatalogItemType() != CatalogItemType.TEMPLATE) {
+ throw WebResourceUtils.preconditionFailed("Item with id '%s:%s' not an entity", symbolicName, version);
+ } else {
+ brooklyn().getCatalog().deleteCatalogItem(item.getSymbolicName(), item.getVersion());
+ }
+ }
+
+ @Override
+ public void deletePolicy(String policyId, String version) throws Exception {
+ if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.MODIFY_CATALOG_ITEM, StringAndArgument.of(policyId+(Strings.isBlank(version) ? "" : ":"+version), "delete"))) {
+ throw WebResourceUtils.unauthorized("User '%s' is not authorized to modify catalog",
+ Entitlements.getEntitlementContext().user());
+ }
+
+ CatalogItem<?, ?> item = mgmt().getCatalog().getCatalogItem(policyId, version);
+ if (item == null) {
+ throw WebResourceUtils.notFound("Policy with id '%s:%s' not found", policyId, version);
+ } else if (item.getCatalogItemType() != CatalogItemType.POLICY) {
+ throw WebResourceUtils.preconditionFailed("Item with id '%s:%s' not a policy", policyId, version);
+ } else {
+ brooklyn().getCatalog().deleteCatalogItem(item.getSymbolicName(), item.getVersion());
+ }
+ }
+
+ @Override
+ public void deleteLocation(String locationId, String version) throws Exception {
+ if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.MODIFY_CATALOG_ITEM, StringAndArgument.of(locationId+(Strings.isBlank(version) ? "" : ":"+version), "delete"))) {
+ throw WebResourceUtils.unauthorized("User '%s' is not authorized to modify catalog",
+ Entitlements.getEntitlementContext().user());
+ }
+
+ CatalogItem<?, ?> item = mgmt().getCatalog().getCatalogItem(locationId, version);
+ if (item == null) {
+ throw WebResourceUtils.notFound("Location with id '%s:%s' not found", locationId, version);
+ } else if (item.getCatalogItemType() != CatalogItemType.LOCATION) {
+ throw WebResourceUtils.preconditionFailed("Item with id '%s:%s' not a location", locationId, version);
+ } else {
+ brooklyn().getCatalog().deleteCatalogItem(item.getSymbolicName(), item.getVersion());
+ }
+ }
+
+ @Override
+ public List<CatalogEntitySummary> listEntities(String regex, String fragment, boolean allVersions) {
+ List<CatalogItemSummary> result = getCatalogItemSummariesMatchingRegexFragment(CatalogPredicates.IS_ENTITY, regex, fragment, allVersions);
+ return castList(result, CatalogEntitySummary.class);
+ }
+
+ @Override
+ public List<CatalogItemSummary> listApplications(String regex, String fragment, boolean allVersions) {
+ Predicate<CatalogItem<Application, EntitySpec<? extends Application>>> filter =
+ Predicates.and(CatalogPredicates.<Application,EntitySpec<? extends Application>>deprecated(false),
+ CatalogPredicates.IS_TEMPLATE);
+ return getCatalogItemSummariesMatchingRegexFragment(filter, regex, fragment, allVersions);
+ }
+
+ @Override
+ @Deprecated
+ public CatalogEntitySummary getEntity(String entityId) {
+ if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_CATALOG_ITEM, entityId)) {
+ throw WebResourceUtils.unauthorized("User '%s' is not authorized to see catalog entry",
+ Entitlements.getEntitlementContext().user());
+ }
+
+ CatalogItem<? extends Entity,EntitySpec<?>> result =
+ CatalogUtils.getCatalogItemOptionalVersion(mgmt(), Entity.class, entityId);
+
+ if (result==null) {
+ throw WebResourceUtils.notFound("Entity with id '%s' not found", entityId);
+ }
+
+ return CatalogTransformer.catalogEntitySummary(brooklyn(), result);
+ }
+
+ @Override
+ public CatalogEntitySummary getEntity(String symbolicName, String version) {
+ if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_CATALOG_ITEM, symbolicName+(Strings.isBlank(version)?"":":"+version))) {
+ throw WebResourceUtils.unauthorized("User '%s' is not authorized to see catalog entry",
+ Entitlements.getEntitlementContext().user());
+ }
+
+ //TODO These casts are not pretty, we could just provide separate get methods for the different types?
+ //Or we could provide asEntity/asPolicy cast methods on the CataloItem doing a safety check internally
+ @SuppressWarnings("unchecked")
+ CatalogItem<? extends Entity, EntitySpec<?>> result =
+ (CatalogItem<? extends Entity, EntitySpec<?>>) brooklyn().getCatalog().getCatalogItem(symbolicName, version);
+
+ if (result==null) {
+ throw WebResourceUtils.notFound("Entity with id '%s:%s' not found", symbolicName, version);
+ }
+
+ return CatalogTransformer.catalogEntitySummary(brooklyn(), result);
+ }
+
+ @Override
+ @Deprecated
+ public CatalogEntitySummary getApplication(String applicationId) throws Exception {
+ return getEntity(applicationId);
+ }
+
+ @Override
+ public CatalogEntitySummary getApplication(String symbolicName, String version) {
+ return getEntity(symbolicName, version);
+ }
+
+ @Override
+ public List<CatalogPolicySummary> listPolicies(String regex, String fragment, boolean allVersions) {
+ List<CatalogItemSummary> result = getCatalogItemSummariesMatchingRegexFragment(CatalogPredicates.IS_POLICY, regex, fragment, allVersions);
+ return castList(result, CatalogPolicySummary.class);
+ }
+
+ @Override
+ @Deprecated
+ public CatalogPolicySummary getPolicy(String policyId) {
+ if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_CATALOG_ITEM, policyId)) {
+ throw WebResourceUtils.unauthorized("User '%s' is not authorized to see catalog entry",
+ Entitlements.getEntitlementContext().user());
+ }
+
+ CatalogItem<? extends Policy, PolicySpec<?>> result =
+ CatalogUtils.getCatalogItemOptionalVersion(mgmt(), Policy.class, policyId);
+
+ if (result==null) {
+ throw WebResourceUtils.notFound("Policy with id '%s' not found", policyId);
+ }
+
+ return CatalogTransformer.catalogPolicySummary(brooklyn(), result);
+ }
+
+ @Override
+ public CatalogPolicySummary getPolicy(String policyId, String version) throws Exception {
+ if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_CATALOG_ITEM, policyId+(Strings.isBlank(version)?"":":"+version))) {
+ throw WebResourceUtils.unauthorized("User '%s' is not authorized to see catalog entry",
+ Entitlements.getEntitlementContext().user());
+ }
+
+ @SuppressWarnings("unchecked")
+ CatalogItem<? extends Policy, PolicySpec<?>> result =
+ (CatalogItem<? extends Policy, PolicySpec<?>>)brooklyn().getCatalog().getCatalogItem(policyId, version);
+
+ if (result==null) {
+ throw WebResourceUtils.notFound("Policy with id '%s:%s' not found", policyId, version);
+ }
+
+ return CatalogTransformer.catalogPolicySummary(brooklyn(), result);
+ }
+
+ @Override
+ public List<CatalogLocationSummary> listLocations(String regex, String fragment, boolean allVersions) {
+ List<CatalogItemSummary> result = getCatalogItemSummariesMatchingRegexFragment(CatalogPredicates.IS_LOCATION, regex, fragment, allVersions);
+ return castList(result, CatalogLocationSummary.class);
+ }
+
+ @Override
+ @Deprecated
+ public CatalogLocationSummary getLocation(String locationId) {
+ if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_CATALOG_ITEM, locationId)) {
+ throw WebResourceUtils.unauthorized("User '%s' is not authorized to see catalog entry",
+ Entitlements.getEntitlementContext().user());
+ }
+
+ CatalogItem<? extends Location, LocationSpec<?>> result =
+ CatalogUtils.getCatalogItemOptionalVersion(mgmt(), Location.class, locationId);
+
+ if (result==null) {
+ throw WebResourceUtils.notFound("Location with id '%s' not found", locationId);
+ }
+
+ return CatalogTransformer.catalogLocationSummary(brooklyn(), result);
+ }
+
+ @Override
+ public CatalogLocationSummary getLocation(String locationId, String version) throws Exception {
+ if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_CATALOG_ITEM, locationId+(Strings.isBlank(version)?"":":"+version))) {
+ throw WebResourceUtils.unauthorized("User '%s' is not authorized to see catalog entry",
+ Entitlements.getEntitlementContext().user());
+ }
+
+ @SuppressWarnings("unchecked")
+ CatalogItem<? extends Location, LocationSpec<?>> result =
+ (CatalogItem<? extends Location, LocationSpec<?>>)brooklyn().getCatalog().getCatalogItem(locationId, version);
+
+ if (result==null) {
+ throw WebResourceUtils.notFound("Location with id '%s:%s' not found", locationId, version);
+ }
+
+ return CatalogTransformer.catalogLocationSummary(brooklyn(), result);
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ private <T,SpecT> List<CatalogItemSummary> getCatalogItemSummariesMatchingRegexFragment(Predicate<CatalogItem<T,SpecT>> type, String regex, String fragment, boolean allVersions) {
+ List filters = new ArrayList();
+ filters.add(type);
+ if (Strings.isNonEmpty(regex))
+ filters.add(CatalogPredicates.xml(StringPredicates.containsRegex(regex)));
+ if (Strings.isNonEmpty(fragment))
+ filters.add(CatalogPredicates.xml(StringPredicates.containsLiteralIgnoreCase(fragment)));
+ if (!allVersions)
+ filters.add(CatalogPredicates.isBestVersion(mgmt()));
+
+ filters.add(CatalogPredicates.entitledToSee(mgmt()));
+
+ ImmutableList<CatalogItem<Object, Object>> sortedItems =
+ FluentIterable.from(brooklyn().getCatalog().getCatalogItems())
+ .filter(Predicates.and(filters))
+ .toSortedList(CatalogItemComparator.getInstance());
+ return Lists.transform(sortedItems, TO_CATALOG_ITEM_SUMMARY);
+ }
+
+ @Override
+ @Deprecated
+ public Response getIcon(String itemId) {
+ if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_CATALOG_ITEM, itemId)) {
+ throw WebResourceUtils.unauthorized("User '%s' is not authorized to see catalog entry",
+ Entitlements.getEntitlementContext().user());
+ }
+
+ CatalogItem<?,?> result = CatalogUtils.getCatalogItemOptionalVersion(mgmt(), itemId);
+ return getCatalogItemIcon(result);
+ }
+
+ @Override
+ public Response getIcon(String itemId, String version) {
+ if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_CATALOG_ITEM, itemId+(Strings.isBlank(version)?"":":"+version))) {
+ throw WebResourceUtils.unauthorized("User '%s' is not authorized to see catalog entry",
+ Entitlements.getEntitlementContext().user());
+ }
+
+ CatalogItem<?,?> result = brooklyn().getCatalog().getCatalogItem(itemId, version);
+ return getCatalogItemIcon(result);
+ }
+
+ @Override
+ public void setDeprecated(String itemId, boolean deprecated) {
+ if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.MODIFY_CATALOG_ITEM, StringAndArgument.of(itemId, "deprecated"))) {
+ throw WebResourceUtils.unauthorized("User '%s' is not authorized to modify catalog",
+ Entitlements.getEntitlementContext().user());
+ }
+ CatalogItem<?, ?> item = CatalogUtils.getCatalogItemOptionalVersion(mgmt(), itemId);
+ if (item==null)
+ throw WebResourceUtils.notFound("Catalog item with id '%s' not found", itemId);
+ item.setDeprecated(deprecated);
+ mgmt().getCatalog().persist(item);
+ }
+
+ private Response getCatalogItemIcon(CatalogItem<?, ?> result) {
+ String url = result.getIconUrl();
+ if (url==null) {
+ log.debug("No icon available for "+result+"; returning "+Status.NO_CONTENT);
+ return Response.status(Status.NO_CONTENT).build();
+ }
+
+ if (brooklyn().isUrlServerSideAndSafe(url)) {
+ // classpath URL's we will serve IF they end with a recognised image format;
+ // paths (ie non-protocol) and
+ // NB, for security, file URL's are NOT served
+ log.debug("Loading and returning "+url+" as icon for "+result);
+
+ MediaType mime = WebResourceUtils.getImageMediaTypeFromExtension(Files.getFileExtension(url));
+ try {
+ Object content = ResourceUtils.create(CatalogUtils.newClassLoadingContext(mgmt(), result)).getResourceFromUrl(url);
+ return Response.ok(content, mime).build();
+ } catch (Exception e) {
+ Exceptions.propagateIfFatal(e);
+ synchronized (missingIcons) {
+ if (missingIcons.add(url)) {
+ // note: this can be quite common when running from an IDE, as resources may not be copied;
+ // a mvn build should sort it out (the IDE will then find the resources, until you clean or maybe refresh...)
+ log.warn("Missing icon data for "+result.getId()+", expected at: "+url+" (subsequent messages will log debug only)");
+ log.debug("Trace for missing icon data at "+url+": "+e, e);
+ } else {
+ log.debug("Missing icon data for "+result.getId()+", expected at: "+url+" (already logged WARN and error details)");
+ }
+ }
+ throw WebResourceUtils.notFound("Icon unavailable for %s", result.getId());
+ }
+ }
+
+ log.debug("Returning redirect to "+url+" as icon for "+result);
+
+ // for anything else we do a redirect (e.g. http / https; perhaps ftp)
+ return Response.temporaryRedirect(URI.create(url)).build();
+ }
+
+ // TODO Move to an appropriate utility class?
+ @SuppressWarnings("unchecked")
+ private static <T> List<T> castList(List<? super T> list, Class<T> elementType) {
+ List<T> result = Lists.newArrayList();
+ Iterator<? super T> li = list.iterator();
+ while (li.hasNext()) {
+ try {
+ result.add((T) li.next());
+ } catch (Throwable throwable) {
+ if (throwable instanceof NoClassDefFoundError) {
+ // happens if class cannot be loaded for any reason during transformation - don't treat as fatal
+ } else {
+ Exceptions.propagateIfFatal(throwable);
+ }
+
+ // item cannot be transformed; we will have logged a warning earlier
+ log.debug("Ignoring invalid catalog item: "+throwable);
+ }
+ }
+ return result;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EffectorResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EffectorResource.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EffectorResource.java
new file mode 100644
index 0000000..c60c808
--- /dev/null
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EffectorResource.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.rest.resources;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import javax.annotation.Nullable;
+import javax.ws.rs.core.Response;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
+
+import brooklyn.entity.Effector;
+import brooklyn.entity.basic.EntityLocal;
+import brooklyn.management.Task;
+import brooklyn.management.entitlement.Entitlements;
+import brooklyn.management.entitlement.Entitlements.StringAndArgument;
+import brooklyn.management.internal.EffectorUtils;
+import brooklyn.rest.api.EffectorApi;
+import brooklyn.rest.domain.EffectorSummary;
+import brooklyn.rest.domain.SummaryComparators;
+import org.apache.brooklyn.rest.filter.HaHotStateRequired;
+import org.apache.brooklyn.rest.transform.EffectorTransformer;
+import org.apache.brooklyn.rest.transform.TaskTransformer;
+import org.apache.brooklyn.rest.util.WebResourceUtils;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.guava.Maybe;
+import brooklyn.util.time.Time;
+
+@HaHotStateRequired
+public class EffectorResource extends AbstractBrooklynRestResource implements EffectorApi {
+
+ private static final Logger log = LoggerFactory.getLogger(EffectorResource.class);
+
+ @Override
+ public List<EffectorSummary> list(final String application, final String entityToken) {
+ final EntityLocal entity = brooklyn().getEntity(application, entityToken);
+ return FluentIterable
+ .from(entity.getEntityType().getEffectors())
+ .filter(new Predicate<Effector<?>>() {
+ @Override
+ public boolean apply(@Nullable Effector<?> input) {
+ return Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.INVOKE_EFFECTOR,
+ Entitlements.EntityAndItem.of(entity, StringAndArgument.of(input.getName(), null)));
+ }
+ })
+ .transform(new Function<Effector<?>, EffectorSummary>() {
+ @Override
+ public EffectorSummary apply(Effector<?> effector) {
+ return EffectorTransformer.effectorSummary(entity, effector);
+ }
+ })
+ .toSortedList(SummaryComparators.nameComparator());
+ }
+
+ @Override
+ public Response invoke(String application, String entityToken, String effectorName,
+ String timeout, Map<String, Object> parameters) {
+ final EntityLocal entity = brooklyn().getEntity(application, entityToken);
+
+ // TODO check effectors?
+ Maybe<Effector<?>> effector = EffectorUtils.findEffectorDeclared(entity, effectorName);
+ if (effector.isAbsentOrNull()) {
+ throw WebResourceUtils.notFound("Entity '%s' has no effector with name '%s'", entityToken, effectorName);
+ } else if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.INVOKE_EFFECTOR,
+ Entitlements.EntityAndItem.of(entity, StringAndArgument.of(effector.get().getName(), null)))) {
+ throw WebResourceUtils.unauthorized("User '%s' is not authorized to invoke effector %s on entity %s",
+ Entitlements.getEntitlementContext().user(), effector.get().getName(), entity);
+ }
+ log.info("REST invocation of " + entity + "." + effector.get() + " " + parameters);
+ Task<?> t = entity.invoke(effector.get(), parameters);
+
+ try {
+ Object result;
+ if (timeout == null || timeout.isEmpty() || "never".equalsIgnoreCase(timeout)) {
+ result = t.get();
+ } else {
+ long timeoutMillis = "always".equalsIgnoreCase(timeout) ? 0 : Time.parseElapsedTime(timeout);
+ try {
+ if (timeoutMillis == 0) throw new TimeoutException();
+ result = t.get(timeoutMillis, TimeUnit.MILLISECONDS);
+ } catch (TimeoutException e) {
+ result = TaskTransformer.taskSummary(t);
+ }
+ }
+ return Response.status(Response.Status.ACCEPTED).entity(result).build();
+ } catch (Exception e) {
+ throw Exceptions.propagate(e);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EntityConfigResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EntityConfigResource.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EntityConfigResource.java
new file mode 100644
index 0000000..bee9d32
--- /dev/null
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EntityConfigResource.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.rest.resources;
+
+import static com.google.common.collect.Iterables.transform;
+
+import java.util.List;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.Entity;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.EntityInternal;
+import brooklyn.entity.basic.EntityLocal;
+import brooklyn.event.basic.BasicConfigKey;
+import brooklyn.management.entitlement.Entitlements;
+import brooklyn.rest.api.EntityConfigApi;
+import brooklyn.rest.domain.EntityConfigSummary;
+import org.apache.brooklyn.rest.filter.HaHotStateRequired;
+import org.apache.brooklyn.rest.transform.EntityTransformer;
+import org.apache.brooklyn.rest.util.WebResourceUtils;
+import brooklyn.util.flags.TypeCoercions;
+import brooklyn.util.task.ValueResolver;
+import brooklyn.util.text.Strings;
+import brooklyn.util.time.Duration;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicates;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+@HaHotStateRequired
+public class EntityConfigResource extends AbstractBrooklynRestResource implements EntityConfigApi {
+
+ private static final Logger LOG = LoggerFactory.getLogger(EntityConfigResource.class);
+
+ @Override
+ public List<EntityConfigSummary> list(final String application, final String entityToken) {
+ final EntityLocal entity = brooklyn().getEntity(application, entityToken);
+ // TODO merge with keys which have values
+ return Lists.newArrayList(transform(
+ entity.getEntityType().getConfigKeys(),
+ new Function<ConfigKey<?>, EntityConfigSummary>() {
+ @Override
+ public EntityConfigSummary apply(ConfigKey<?> config) {
+ return EntityTransformer.entityConfigSummary(entity, config);
+ }
+ }));
+ }
+
+ // TODO support parameters ?show=value,summary&name=xxx &format={string,json,xml}
+ // (and in sensors class)
+ @Override
+ public Map<String, Object> batchConfigRead(String application, String entityToken, Boolean raw) {
+ // TODO: add test
+ EntityLocal entity = brooklyn().getEntity(application, entityToken);
+ Map<ConfigKey<?>, ?> source = ((EntityInternal) entity).config().getBag().getAllConfigAsConfigKeyMap();
+ Map<String, Object> result = Maps.newLinkedHashMap();
+ for (Map.Entry<ConfigKey<?>, ?> ek : source.entrySet()) {
+ Object value = ek.getValue();
+ result.put(ek.getKey().getName(),
+ resolving(value).preferJson(true).asJerseyOutermostReturnValue(false).raw(raw).context(entity).timeout(Duration.ZERO).renderAs(ek.getKey()).resolve());
+ }
+ return result;
+ }
+
+ @Override
+ public Object get(String application, String entityToken, String configKeyName, Boolean raw) {
+ return get(true, application, entityToken, configKeyName, raw);
+ }
+
+ @Override
+ public String getPlain(String application, String entityToken, String configKeyName, Boolean raw) {
+ return Strings.toString(get(false, application, entityToken, configKeyName, raw));
+ }
+
+ public Object get(boolean preferJson, String application, String entityToken, String configKeyName, Boolean raw) {
+ EntityLocal entity = brooklyn().getEntity(application, entityToken);
+ ConfigKey<?> ck = findConfig(entity, configKeyName);
+ Object value = ((EntityInternal)entity).config().getRaw(ck).orNull();
+ return resolving(value).preferJson(preferJson).asJerseyOutermostReturnValue(true).raw(raw).context(entity).timeout(ValueResolver.PRETTY_QUICK_WAIT).renderAs(ck).resolve();
+ }
+
+ private ConfigKey<?> findConfig(EntityLocal entity, String configKeyName) {
+ ConfigKey<?> ck = entity.getEntityType().getConfigKey(configKeyName);
+ if (ck == null)
+ ck = new BasicConfigKey<Object>(Object.class, configKeyName);
+ return ck;
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ @Override
+ public void setFromMap(String application, String entityToken, Boolean recurse, Map newValues) {
+ final EntityLocal entity = brooklyn().getEntity(application, entityToken);
+ if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.MODIFY_ENTITY, entity)) {
+ throw WebResourceUtils.unauthorized("User '%s' is not authorized to modify entity '%s'",
+ Entitlements.getEntitlementContext().user(), entity);
+ }
+
+ if (LOG.isDebugEnabled())
+ LOG.debug("REST user " + Entitlements.getEntitlementContext() + " setting configs " + newValues);
+ for (Object entry : newValues.entrySet()) {
+ String configName = Strings.toString(((Map.Entry) entry).getKey());
+ Object newValue = ((Map.Entry) entry).getValue();
+
+ ConfigKey ck = findConfig(entity, configName);
+ ((EntityInternal) entity).config().set(ck, TypeCoercions.coerce(newValue, ck.getTypeToken()));
+ if (Boolean.TRUE.equals(recurse)) {
+ for (Entity e2 : Entities.descendants(entity, Predicates.alwaysTrue(), false)) {
+ ((EntityInternal) e2).config().set(ck, newValue);
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ @Override
+ public void set(String application, String entityToken, String configName, Boolean recurse, Object newValue) {
+ final EntityLocal entity = brooklyn().getEntity(application, entityToken);
+ if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.MODIFY_ENTITY, entity)) {
+ throw WebResourceUtils.unauthorized("User '%s' is not authorized to modify entity '%s'",
+ Entitlements.getEntitlementContext().user(), entity);
+ }
+
+ ConfigKey ck = findConfig(entity, configName);
+ LOG.debug("REST setting config " + configName + " on " + entity + " to " + newValue);
+ ((EntityInternal) entity).config().set(ck, TypeCoercions.coerce(newValue, ck.getTypeToken()));
+ if (Boolean.TRUE.equals(recurse)) {
+ for (Entity e2 : Entities.descendants(entity, Predicates.alwaysTrue(), false)) {
+ ((EntityInternal) e2).config().set(ck, newValue);
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EntityResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EntityResource.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EntityResource.java
new file mode 100644
index 0000000..44ce884
--- /dev/null
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EntityResource.java
@@ -0,0 +1,225 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.rest.resources;
+
+import static javax.ws.rs.core.Response.created;
+import static javax.ws.rs.core.Response.status;
+import static javax.ws.rs.core.Response.Status.ACCEPTED;
+
+import java.net.URI;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
+import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.UriInfo;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.entity.Entity;
+import brooklyn.entity.basic.BrooklynTags;
+import brooklyn.entity.basic.BrooklynTags.NamedStringTag;
+import brooklyn.entity.basic.BrooklynTaskTags;
+import brooklyn.entity.basic.EntityLocal;
+import brooklyn.location.Location;
+import brooklyn.management.Task;
+import brooklyn.management.entitlement.EntitlementPredicates;
+import brooklyn.management.entitlement.Entitlements;
+import brooklyn.management.internal.EntityManagementUtils;
+import brooklyn.management.internal.EntityManagementUtils.CreationResult;
+import brooklyn.rest.api.EntityApi;
+import brooklyn.rest.domain.EntitySummary;
+import brooklyn.rest.domain.LocationSummary;
+import brooklyn.rest.domain.TaskSummary;
+import org.apache.brooklyn.rest.filter.HaHotStateRequired;
+import org.apache.brooklyn.rest.transform.EntityTransformer;
+import org.apache.brooklyn.rest.transform.LocationTransformer;
+import org.apache.brooklyn.rest.transform.LocationTransformer.LocationDetailLevel;
+import org.apache.brooklyn.rest.transform.TaskTransformer;
+import org.apache.brooklyn.rest.util.WebResourceUtils;
+import brooklyn.util.ResourceUtils;
+import brooklyn.util.collections.MutableList;
+import brooklyn.util.time.Duration;
+
+import com.google.common.collect.Collections2;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.io.Files;
+
+@HaHotStateRequired
+public class EntityResource extends AbstractBrooklynRestResource implements EntityApi {
+
+ private static final Logger log = LoggerFactory.getLogger(EntityResource.class);
+
+ @Context
+ private UriInfo uriInfo;
+
+ @Override
+ public List<EntitySummary> list(final String application) {
+ return FluentIterable
+ .from(brooklyn().getApplication(application).getChildren())
+ .filter(EntitlementPredicates.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_ENTITY))
+ .transform(EntityTransformer.FROM_ENTITY)
+ .toList();
+ }
+
+ @Override
+ public EntitySummary get(String application, String entityName) {
+ Entity entity = brooklyn().getEntity(application, entityName);
+ if (Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_ENTITY, entity)) {
+ return EntityTransformer.entitySummary(entity);
+ }
+ throw WebResourceUtils.unauthorized("User '%s' is not authorized to get entity '%s'",
+ Entitlements.getEntitlementContext().user(), entity);
+ }
+
+ @Override
+ public List<EntitySummary> getChildren(final String application, final String entity) {
+ return FluentIterable
+ .from(brooklyn().getEntity(application, entity).getChildren())
+ .filter(EntitlementPredicates.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_ENTITY))
+ .transform(EntityTransformer.FROM_ENTITY)
+ .toList();
+ }
+
+ @Override
+ public List<EntitySummary> getChildrenOld(String application, String entity) {
+ log.warn("Using deprecated call to /entities when /children should be used");
+ return getChildren(application, entity);
+ }
+
+ @Override
+ public Response addChildren(String applicationToken, String entityToken, Boolean start, String timeoutS, String yaml) {
+ final EntityLocal parent = brooklyn().getEntity(applicationToken, entityToken);
+ if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.MODIFY_ENTITY, parent)) {
+ throw WebResourceUtils.unauthorized("User '%s' is not authorized to modify entity '%s'",
+ Entitlements.getEntitlementContext().user(), entityToken);
+ }
+ CreationResult<List<Entity>, List<String>> added = EntityManagementUtils.addChildren(parent, yaml, start)
+ .blockUntilComplete(timeoutS==null ? Duration.millis(20) : Duration.of(timeoutS));
+ ResponseBuilder response;
+
+ if (added.get().size()==1) {
+ Entity child = Iterables.getOnlyElement(added.get());
+ URI ref = uriInfo.getBaseUriBuilder()
+ .path(EntityApi.class)
+ .path(EntityApi.class, "get")
+ .build(child.getApplicationId(), child.getId());
+ response = created(ref);
+ } else {
+ response = Response.status(Status.CREATED);
+ }
+ return response.entity(TaskTransformer.taskSummary(added.task())).build();
+ }
+
+ @Override
+ public List<TaskSummary> listTasks(String applicationId, String entityId) {
+ Entity entity = brooklyn().getEntity(applicationId, entityId);
+ Set<Task<?>> tasks = BrooklynTaskTags.getTasksInEntityContext(mgmt().getExecutionManager(), entity);
+ return new LinkedList<TaskSummary>(Collections2.transform(tasks, TaskTransformer.FROM_TASK));
+ }
+
+ @Override
+ public TaskSummary getTask(final String application, final String entityToken, String taskId) {
+ // TODO deprecate in favour of ActivityApi.get ?
+ Task<?> t = mgmt().getExecutionManager().getTask(taskId);
+ if (t == null)
+ throw WebResourceUtils.notFound("Cannot find task '%s'", taskId);
+ return TaskTransformer.FROM_TASK.apply(t);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public List<Object> listTags(String applicationId, String entityId) {
+ Entity entity = brooklyn().getEntity(applicationId, entityId);
+ return (List<Object>) resolving(MutableList.copyOf(entity.tags().getTags())).preferJson(true).resolve();
+ }
+
+ @Override
+ public Response getIcon(String applicationId, String entityId) {
+ EntityLocal entity = brooklyn().getEntity(applicationId, entityId);
+ String url = entity.getIconUrl();
+ if (url == null)
+ return Response.status(Status.NO_CONTENT).build();
+
+ if (brooklyn().isUrlServerSideAndSafe(url)) {
+ // classpath URL's we will serve IF they end with a recognised image format;
+ // paths (ie non-protocol) and
+ // NB, for security, file URL's are NOT served
+ MediaType mime = WebResourceUtils.getImageMediaTypeFromExtension(Files.getFileExtension(url));
+ Object content = ResourceUtils.create(brooklyn().getCatalogClassLoader()).getResourceFromUrl(url);
+ return Response.ok(content, mime).build();
+ }
+
+ // for anything else we do a redirect (e.g. http / https; perhaps ftp)
+ return Response.temporaryRedirect(URI.create(url)).build();
+ }
+
+ @Override
+ public Response rename(String application, String entity, String newName) {
+ EntityLocal entityLocal = brooklyn().getEntity(application, entity);
+ entityLocal.setDisplayName(newName);
+ return status(Response.Status.OK).build();
+ }
+
+ @Override
+ public Response expunge(String application, String entity, boolean release) {
+ EntityLocal entityLocal = brooklyn().getEntity(application, entity);
+ Task<?> task = brooklyn().expunge(entityLocal, release);
+ TaskSummary summary = TaskTransformer.FROM_TASK.apply(task);
+ return status(ACCEPTED).entity(summary).build();
+ }
+
+ @Override
+ public List<EntitySummary> getDescendants(String application, String entity, String typeRegex) {
+ return EntityTransformer.entitySummaries(brooklyn().descendantsOfType(application, entity, typeRegex));
+ }
+
+ @Override
+ public Map<String, Object> getDescendantsSensor(String application, String entity, String sensor, String typeRegex) {
+ Iterable<Entity> descs = brooklyn().descendantsOfType(application, entity, typeRegex);
+ return ApplicationResource.getSensorMap(sensor, descs);
+ }
+
+ @Override
+ public List<LocationSummary> getLocations(String application, String entity) {
+ List<LocationSummary> result = Lists.newArrayList();
+ EntityLocal e = brooklyn().getEntity(application, entity);
+ for (Location l : e.getLocations()) {
+ result.add(LocationTransformer.newInstance(mgmt(), l, LocationDetailLevel.NONE));
+ }
+ return result;
+ }
+
+ @Override
+ public String getSpec(String applicationToken, String entityToken) {
+ EntityLocal entity = brooklyn().getEntity(applicationToken, entityToken);
+ NamedStringTag spec = BrooklynTags.findFirst(BrooklynTags.YAML_SPEC_KIND, entity.tags().getTags());
+ if (spec == null)
+ return null;
+ return (String) WebResourceUtils.getValueForDisplay(spec.getContents(), true, true);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/LocationResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/LocationResource.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/LocationResource.java
new file mode 100644
index 0000000..5618683
--- /dev/null
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/LocationResource.java
@@ -0,0 +1,185 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.rest.resources;
+
+import java.net.URI;
+import java.util.Comparator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.ws.rs.core.Response;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.brooklyn.catalog.CatalogItem;
+import brooklyn.catalog.internal.CatalogUtils;
+import brooklyn.location.Location;
+import brooklyn.location.LocationDefinition;
+import brooklyn.location.basic.LocationConfigKeys;
+import brooklyn.rest.api.LocationApi;
+import brooklyn.rest.domain.LocationSpec;
+import brooklyn.rest.domain.LocationSummary;
+import org.apache.brooklyn.rest.filter.HaHotStateRequired;
+import org.apache.brooklyn.rest.transform.LocationTransformer;
+import org.apache.brooklyn.rest.transform.LocationTransformer.LocationDetailLevel;
+import org.apache.brooklyn.rest.util.EntityLocationUtils;
+import org.apache.brooklyn.rest.util.WebResourceUtils;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.text.NaturalOrderComparator;
+import brooklyn.util.text.Strings;
+
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
+
+@SuppressWarnings("deprecation")
+@HaHotStateRequired
+public class LocationResource extends AbstractBrooklynRestResource implements LocationApi {
+
+ private static final Logger log = LoggerFactory.getLogger(LocationResource.class);
+
+ private final Set<String> specsWarnedOnException = Sets.newConcurrentHashSet();
+
+ @Override
+ public List<LocationSummary> list() {
+ Function<LocationDefinition, LocationSummary> transformer = new Function<LocationDefinition, LocationSummary>() {
+ @Override
+ public LocationSummary apply(LocationDefinition l) {
+ try {
+ return LocationTransformer.newInstance(mgmt(), l, LocationDetailLevel.LOCAL_EXCLUDING_SECRET);
+ } catch (Exception e) {
+ Exceptions.propagateIfFatal(e);
+ String spec = l.getSpec();
+ if (spec == null || specsWarnedOnException.add(spec)) {
+ log.warn("Unable to find details of location {} in REST call to list (ignoring location): {}", l, e);
+ if (log.isDebugEnabled()) log.debug("Error details for location " + l, e);
+ } else {
+ if (log.isTraceEnabled())
+ log.trace("Unable again to find details of location {} in REST call to list (ignoring location): {}", l, e);
+ }
+ return null;
+ }
+ }
+ };
+ return FluentIterable.from(brooklyn().getLocationRegistry().getDefinedLocations().values())
+ .transform(transformer)
+ .filter(LocationSummary.class)
+ .toSortedList(nameOrSpecComparator());
+ }
+
+ private static NaturalOrderComparator COMPARATOR = new NaturalOrderComparator();
+ private static Comparator<LocationSummary> nameOrSpecComparator() {
+ return new Comparator<LocationSummary>() {
+ @Override
+ public int compare(LocationSummary o1, LocationSummary o2) {
+ return COMPARATOR.compare(getNameOrSpec(o1).toLowerCase(), getNameOrSpec(o2).toLowerCase());
+ }
+ };
+ }
+ private static String getNameOrSpec(LocationSummary o) {
+ if (Strings.isNonBlank(o.getName())) return o.getName();
+ if (Strings.isNonBlank(o.getSpec())) return o.getSpec();
+ return o.getId();
+ }
+
+ // this is here to support the web GUI's circles
+ @Override
+ public Map<String,Map<String,Object>> getLocatedLocations() {
+ Map<String,Map<String,Object>> result = new LinkedHashMap<String,Map<String,Object>>();
+ Map<Location, Integer> counts = new EntityLocationUtils(mgmt()).countLeafEntitiesByLocatedLocations();
+ for (Map.Entry<Location,Integer> count: counts.entrySet()) {
+ Location l = count.getKey();
+ Map<String,Object> m = MutableMap.<String,Object>of(
+ "id", l.getId(),
+ "name", l.getDisplayName(),
+ "leafEntityCount", count.getValue(),
+ "latitude", l.getConfig(LocationConfigKeys.LATITUDE),
+ "longitude", l.getConfig(LocationConfigKeys.LONGITUDE)
+ );
+ result.put(l.getId(), m);
+ }
+ return result;
+ }
+
+ /** @deprecated since 0.7.0; REST call now handled by below (optional query parameter added) */
+ public LocationSummary get(String locationId) {
+ return get(locationId, false);
+ }
+
+ @Override
+ public LocationSummary get(String locationId, String fullConfig) {
+ return get(locationId, Boolean.valueOf(fullConfig));
+ }
+
+ public LocationSummary get(String locationId, boolean fullConfig) {
+ LocationDetailLevel configLevel = fullConfig ? LocationDetailLevel.FULL_EXCLUDING_SECRET : LocationDetailLevel.LOCAL_EXCLUDING_SECRET;
+ Location l1 = mgmt().getLocationManager().getLocation(locationId);
+ if (l1!=null) {
+ return LocationTransformer.newInstance(mgmt(), l1, configLevel);
+ }
+
+ LocationDefinition l2 = brooklyn().getLocationRegistry().getDefinedLocationById(locationId);
+ if (l2==null) throw WebResourceUtils.notFound("No location matching %s", locationId);
+ return LocationTransformer.newInstance(mgmt(), l2, configLevel);
+ }
+
+ @Override
+ public Response create(LocationSpec locationSpec) {
+ String name = locationSpec.getName();
+ ImmutableList.Builder<String> yaml = ImmutableList.<String>builder().add(
+ "brooklyn.catalog:",
+ " symbolicName: "+name,
+ "",
+ "brooklyn.locations:",
+ "- type: "+locationSpec.getSpec());
+ if (locationSpec.getConfig().size() > 0) {
+ yaml.add(" brooklyn.config:");
+ for (Map.Entry<String, ?> entry : locationSpec.getConfig().entrySet()) {
+ yaml.add(" "+entry.getKey()+": "+entry.getValue());
+ }
+ }
+
+ brooklyn().getCatalog().addItems(Joiner.on("\n").join(yaml.build()));
+ LocationDefinition l = brooklyn().getLocationRegistry().getDefinedLocationByName(name);
+ return Response.created(URI.create(name))
+ .entity(LocationTransformer.newInstance(mgmt(), l, LocationDetailLevel.LOCAL_EXCLUDING_SECRET))
+ .build();
+ }
+
+ @Override
+ @Deprecated
+ public void delete(String locationId) {
+ // TODO make all locations be part of the catalog, then flip the JS GUI to use catalog api
+ if (deleteAllVersions(locationId)>0) return;
+ throw WebResourceUtils.notFound("No catalog item location matching %s; only catalog item locations can be deleted", locationId);
+ }
+
+ private int deleteAllVersions(String locationId) {
+ CatalogItem<?, ?> item = CatalogUtils.getCatalogItemOptionalVersion(mgmt(), locationId);
+ if (item==null) return 0;
+ brooklyn().getCatalog().deleteCatalogItem(item.getSymbolicName(), item.getVersion());
+ return 1 + deleteAllVersions(locationId);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/PolicyConfigResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/PolicyConfigResource.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/PolicyConfigResource.java
new file mode 100644
index 0000000..359810b
--- /dev/null
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/PolicyConfigResource.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.rest.resources;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.core.Response;
+
+import brooklyn.basic.BrooklynObjectInternal;
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.Entity;
+import brooklyn.entity.basic.EntityLocal;
+import brooklyn.management.entitlement.Entitlements;
+import brooklyn.policy.Policy;
+import brooklyn.rest.api.PolicyConfigApi;
+import brooklyn.rest.domain.PolicyConfigSummary;
+import org.apache.brooklyn.rest.filter.HaHotStateRequired;
+import org.apache.brooklyn.rest.transform.PolicyTransformer;
+import org.apache.brooklyn.rest.util.BrooklynRestResourceUtils;
+import org.apache.brooklyn.rest.util.WebResourceUtils;
+import brooklyn.util.flags.TypeCoercions;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+@HaHotStateRequired
+public class PolicyConfigResource extends AbstractBrooklynRestResource implements PolicyConfigApi {
+
+ @Override
+ public List<PolicyConfigSummary> list(
+ final String application, final String entityToken, final String policyToken) {
+ EntityLocal entity = brooklyn().getEntity(application, entityToken);
+ Policy policy = brooklyn().getPolicy(entity, policyToken);
+
+ List<PolicyConfigSummary> result = Lists.newArrayList();
+ for (ConfigKey<?> key : policy.getPolicyType().getConfigKeys()) {
+ result.add(PolicyTransformer.policyConfigSummary(brooklyn(), entity, policy, key));
+ }
+ return result;
+ }
+
+ // TODO support parameters ?show=value,summary&name=xxx &format={string,json,xml}
+ // (and in sensors class)
+ @Override
+ public Map<String, Object> batchConfigRead(String application, String entityToken, String policyToken) {
+ // TODO: add test
+ Policy policy = brooklyn().getPolicy(application, entityToken, policyToken);
+ Map<String, Object> source = ((BrooklynObjectInternal)policy).config().getBag().getAllConfig();
+ Map<String, Object> result = Maps.newLinkedHashMap();
+ for (Map.Entry<String, Object> ek : source.entrySet()) {
+ result.put(ek.getKey(), getStringValueForDisplay(brooklyn(), policy, ek.getValue()));
+ }
+ return result;
+ }
+
+ @Override
+ public String get(String application, String entityToken, String policyToken, String configKeyName) {
+ Policy policy = brooklyn().getPolicy(application, entityToken, policyToken);
+ ConfigKey<?> ck = policy.getPolicyType().getConfigKey(configKeyName);
+ if (ck == null) throw WebResourceUtils.notFound("Cannot find config key '%s' in policy '%s' of entity '%s'", configKeyName, policy, entityToken);
+
+ return getStringValueForDisplay(brooklyn(), policy, policy.getConfig(ck));
+ }
+
+ @Override
+ @Deprecated
+ public Response set(String application, String entityToken, String policyToken, String configKeyName, String value) {
+ return set(application, entityToken, policyToken, configKeyName, (Object) value);
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ @Override
+ public Response set(String application, String entityToken, String policyToken, String configKeyName, Object value) {
+ Entity entity = brooklyn().getEntity(application, entityToken);
+ if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.MODIFY_ENTITY, entity)) {
+ throw WebResourceUtils.unauthorized("User '%s' is not authorized to modify entity '%s'",
+ Entitlements.getEntitlementContext().user(), entity);
+ }
+
+ Policy policy = brooklyn().getPolicy(application, entityToken, policyToken);
+ ConfigKey<?> ck = policy.getPolicyType().getConfigKey(configKeyName);
+ if (ck == null) throw WebResourceUtils.notFound("Cannot find config key '%s' in policy '%s' of entity '%s'", configKeyName, policy, entityToken);
+
+ policy.config().set((ConfigKey) ck, TypeCoercions.coerce(value, ck.getTypeToken()));
+
+ return Response.status(Response.Status.OK).build();
+ }
+
+ public static String getStringValueForDisplay(BrooklynRestResourceUtils utils, Policy policy, Object value) {
+ return utils.getStringValueForDisplay(value);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/PolicyResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/PolicyResource.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/PolicyResource.java
new file mode 100644
index 0000000..341dd43
--- /dev/null
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/PolicyResource.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.rest.resources;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.core.Response;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.entity.Entity;
+import brooklyn.entity.basic.EntityLocal;
+import brooklyn.policy.Policy;
+import brooklyn.policy.PolicySpec;
+import brooklyn.policy.basic.Policies;
+import brooklyn.rest.api.PolicyApi;
+import brooklyn.rest.domain.PolicySummary;
+import brooklyn.rest.domain.Status;
+import brooklyn.rest.domain.SummaryComparators;
+import org.apache.brooklyn.rest.filter.HaHotStateRequired;
+import org.apache.brooklyn.rest.transform.ApplicationTransformer;
+import org.apache.brooklyn.rest.transform.PolicyTransformer;
+import org.apache.brooklyn.rest.util.WebResourceUtils;
+import brooklyn.util.exceptions.Exceptions;
+
+import com.google.common.base.Function;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.Maps;
+
+@HaHotStateRequired
+public class PolicyResource extends AbstractBrooklynRestResource implements PolicyApi {
+
+ private static final Logger log = LoggerFactory.getLogger(PolicyResource.class);
+
+ @Override
+ public List<PolicySummary> list( final String application, final String entityToken) {
+ final Entity entity = brooklyn().getEntity(application, entityToken);
+ return FluentIterable.from(entity.getPolicies())
+ .transform(new Function<Policy, PolicySummary>() {
+ @Override
+ public PolicySummary apply(Policy policy) {
+ return PolicyTransformer.policySummary(entity, policy);
+ }
+ })
+ .toSortedList(SummaryComparators.nameComparator());
+ }
+
+ // TODO support parameters ?show=value,summary&name=xxx
+ // (and in sensors class)
+ @Override
+ public Map<String, Boolean> batchConfigRead( String application, String entityToken) {
+ // TODO: add test
+ EntityLocal entity = brooklyn().getEntity(application, entityToken);
+ Map<String, Boolean> result = Maps.newLinkedHashMap();
+ for (Policy p : entity.getPolicies()) {
+ result.put(p.getId(), !p.isSuspended());
+ }
+ return result;
+ }
+
+ // TODO would like to make 'config' arg optional but jersey complains if we do
+ @SuppressWarnings("unchecked")
+ @Override
+ public PolicySummary addPolicy( String application,String entityToken, String policyTypeName,
+ Map<String, String> config) {
+ EntityLocal entity = brooklyn().getEntity(application, entityToken);
+ Class<? extends Policy> policyType;
+ try {
+ policyType = (Class<? extends Policy>) Class.forName(policyTypeName);
+ } catch (ClassNotFoundException e) {
+ throw WebResourceUtils.badRequest("No policy with type %s found", policyTypeName);
+ } catch (ClassCastException e) {
+ throw WebResourceUtils.badRequest("No policy with type %s found", policyTypeName);
+ } catch (Exception e) {
+ throw Exceptions.propagate(e);
+ }
+
+ Policy policy = entity.addPolicy(PolicySpec.create(policyType).configure(config));
+ log.debug("REST API added policy " + policy + " to " + entity);
+
+ return PolicyTransformer.policySummary(entity, policy);
+ }
+
+ @Override
+ public Status getStatus(String application, String entityToken, String policyId) {
+ Policy policy = brooklyn().getPolicy(application, entityToken, policyId);
+ return ApplicationTransformer.statusFromLifecycle(Policies.getPolicyStatus(policy));
+ }
+
+ @Override
+ public Response start( String application, String entityToken, String policyId) {
+ Policy policy = brooklyn().getPolicy(application, entityToken, policyId);
+
+ policy.resume();
+ return Response.status(Response.Status.NO_CONTENT).build();
+ }
+
+ @Override
+ public Response stop(String application, String entityToken, String policyId) {
+ Policy policy = brooklyn().getPolicy(application, entityToken, policyId);
+
+ policy.suspend();
+ return Response.status(Response.Status.NO_CONTENT).build();
+ }
+
+ @Override
+ public Response destroy(String application, String entityToken, String policyToken) {
+ EntityLocal entity = brooklyn().getEntity(application, entityToken);
+ Policy policy = brooklyn().getPolicy(entity, policyToken);
+
+ policy.suspend();
+ entity.removePolicy(policy);
+ return Response.status(Response.Status.NO_CONTENT).build();
+ }
+}