add adjunct REST API, deprecating policy endpoint
Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/45261c7e Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/45261c7e Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/45261c7e Branch: refs/heads/master Commit: 45261c7e155e1022cd172d9816a647ba35f0218c Parents: 2860af5 Author: Alex Heneveld <[email protected]> Authored: Wed Sep 13 16:22:56 2017 +0100 Committer: Alex Heneveld <[email protected]> Committed: Wed Sep 13 17:34:53 2017 +0100 ---------------------------------------------------------------------- .../org/apache/brooklyn/api/sensor/Feed.java | 2 +- .../brooklyn/core/entity/AbstractEntity.java | 5 + .../brooklyn/core/entity/EntityInternal.java | 2 +- .../apache/brooklyn/core/policy/Policies.java | 30 ++- .../apache/brooklyn/rest/api/AdjunctApi.java | 233 ++++++++++++++++ .../org/apache/brooklyn/rest/api/PolicyApi.java | 2 + .../brooklyn/rest/api/PolicyConfigApi.java | 27 +- .../brooklyn/rest/domain/AdjunctDetail.java | 72 +++++ .../brooklyn/rest/domain/AdjunctSummary.java | 150 +++++++++++ .../rest/domain/CatalogEnricherSummary.java | 2 + .../rest/domain/CatalogEntitySummary.java | 2 + .../rest/domain/CatalogItemSummary.java | 2 + .../rest/domain/CatalogLocationSummary.java | 2 + .../rest/domain/CatalogPolicySummary.java | 2 + .../rest/domain/EnricherConfigSummary.java | 3 +- .../rest/domain/PolicyConfigSummary.java | 3 +- .../brooklyn/rest/domain/PolicySummary.java | 83 +----- .../org/apache/brooklyn/rest/domain/Status.java | 7 +- .../apache/brooklyn/rest/BrooklynRestApi.java | 2 + .../rest/resources/AdjunctResource.java | 263 +++++++++++++++++++ .../rest/resources/PolicyConfigResource.java | 1 + .../brooklyn/rest/resources/PolicyResource.java | 1 + .../rest/transform/AdjunctTransformer.java | 117 +++++++++ .../rest/transform/EntityTransformer.java | 21 ++ .../rest/transform/PolicyTransformer.java | 3 + .../rest/util/BrooklynRestResourceUtils.java | 43 +++ 26 files changed, 983 insertions(+), 97 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/api/src/main/java/org/apache/brooklyn/api/sensor/Feed.java ---------------------------------------------------------------------- diff --git a/api/src/main/java/org/apache/brooklyn/api/sensor/Feed.java b/api/src/main/java/org/apache/brooklyn/api/sensor/Feed.java index d50e092..01a4714 100644 --- a/api/src/main/java/org/apache/brooklyn/api/sensor/Feed.java +++ b/api/src/main/java/org/apache/brooklyn/api/sensor/Feed.java @@ -43,7 +43,7 @@ public interface Feed extends EntityAdjunct, Rebindable { /** * True if everything has been _started_ (or it is starting) but not stopped, - * even if it is suspended; see also {@link #isActive()} + * even if it is suspended; see also {@link #isRunning()} */ boolean isActivated(); http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java b/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java index 08b6591..8c31512 100644 --- a/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java +++ b/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java @@ -1951,6 +1951,11 @@ public abstract class AbstractEntity extends AbstractBrooklynObject implements E } return changed; } + + @Override + public Iterator<Feed> iterator() { + return getFeeds().iterator(); + } } // -------- SENSORS -------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/core/src/main/java/org/apache/brooklyn/core/entity/EntityInternal.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/EntityInternal.java b/core/src/main/java/org/apache/brooklyn/core/entity/EntityInternal.java index d6d51a1..7377f38 100644 --- a/core/src/main/java/org/apache/brooklyn/core/entity/EntityInternal.java +++ b/core/src/main/java/org/apache/brooklyn/core/entity/EntityInternal.java @@ -189,7 +189,7 @@ public interface EntityInternal extends BrooklynObjectInternal, EntityLocal, Reb void remove(AttributeSensor<?> attribute); } - public interface FeedSupport { + public interface FeedSupport extends Iterable<Feed> { Collection<Feed> getFeeds(); http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/core/src/main/java/org/apache/brooklyn/core/policy/Policies.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/policy/Policies.java b/core/src/main/java/org/apache/brooklyn/core/policy/Policies.java index db9bece..4b32dfc 100644 --- a/core/src/main/java/org/apache/brooklyn/core/policy/Policies.java +++ b/core/src/main/java/org/apache/brooklyn/core/policy/Policies.java @@ -20,7 +20,9 @@ package org.apache.brooklyn.core.policy; import org.apache.brooklyn.api.entity.Entity; import org.apache.brooklyn.api.entity.EntityLocal; +import org.apache.brooklyn.api.objs.EntityAdjunct; import org.apache.brooklyn.api.policy.Policy; +import org.apache.brooklyn.api.sensor.Feed; import org.apache.brooklyn.api.sensor.Sensor; import org.apache.brooklyn.api.sensor.SensorEvent; import org.apache.brooklyn.api.sensor.SensorEventListener; @@ -28,6 +30,8 @@ import org.apache.brooklyn.core.entity.lifecycle.Lifecycle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.annotations.Beta; + import groovy.lang.Closure; @SuppressWarnings({"rawtypes","unchecked"}) @@ -81,11 +85,27 @@ public class Policies { } public static Lifecycle getPolicyStatus(Policy p) { - if (p.isRunning()) return Lifecycle.RUNNING; - if (p.isDestroyed()) return Lifecycle.DESTROYED; - if (p.isSuspended()) return Lifecycle.STOPPED; - // TODO could policy be in an error state? - return Lifecycle.CREATED; + return inferAdjunctStatus(p); + } + + @Beta + public static Lifecycle inferAdjunctStatus(EntityAdjunct a) { + if (a.isRunning()) return Lifecycle.RUNNING; + if (a.isDestroyed()) return Lifecycle.DESTROYED; + + // adjuncts don't currently support an "error" state; though that would be useful! + + if (a instanceof Policy) { + if (((Policy)a).isSuspended()) return Lifecycle.STOPPED; + return Lifecycle.CREATED; + } + if (a instanceof Feed) { + if (((Feed)a).isSuspended()) return Lifecycle.STOPPED; + if (((Feed)a).isActivated()) return Lifecycle.STARTING; + return Lifecycle.CREATED; + } + + return Lifecycle.STOPPED; } } http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/AdjunctApi.java ---------------------------------------------------------------------- diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/AdjunctApi.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/AdjunctApi.java new file mode 100644 index 0000000..5fc1366 --- /dev/null +++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/AdjunctApi.java @@ -0,0 +1,233 @@ +/* + * 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.api; + +import java.util.List; +import java.util.Map; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.apache.brooklyn.rest.domain.AdjunctConfigSummary; +import org.apache.brooklyn.rest.domain.AdjunctDetail; +import org.apache.brooklyn.rest.domain.AdjunctSummary; +import org.apache.brooklyn.rest.domain.Status; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + +@Path("/applications/{application}/entities/{entity}/adjuncts") +@Api("Entity Adjuncts (policies, enrichers, feeds)") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public interface AdjunctApi { + + @GET + @ApiOperation(value = "Fetch the adjuncts attached to a specific application entity", + response = org.apache.brooklyn.rest.domain.AdjunctSummary.class, + responseContainer = "List") + @ApiResponses(value = { + @ApiResponse(code = 404, message = "Could not find application or entity") + }) + public List<AdjunctSummary> list( + @ApiParam(value = "Application ID or name", required = true) + @PathParam("application") final String application, + @ApiParam(value = "Entity ID or name", required = true) + @PathParam("entity") final String entityToken, + @ApiParam(value = "Filter by adjunct type", required = false) + @QueryParam("adjunctType") final String adjunctType); + + @POST + @ApiOperation(value = "Add an adjunct (policy, enricher, or feed)", notes = "Returns a summary of the added adjunct") + @ApiResponses(value = { + @ApiResponse(code = 404, message = "Could not find application or entity"), + @ApiResponse(code = 400, message = "Type is not a suitable adjunct") + }) + public AdjunctSummary addAdjunct( + @ApiParam(name = "application", value = "Application ID or name", required = true) + @PathParam("application") String application, + + @ApiParam(name = "entity", value = "Entity ID or name", required = true) + @PathParam("entity") String entityToken, + + @ApiParam(name = "type", value = "Adjunct from the type registry to instantiate and add", required = true) + @QueryParam("type") + String adjunctRegisteredTypeName, + + // TODO would like to make this optional but jersey complains if we do + @ApiParam(name = "config", value = "Configuration for the adjunct (as key value pairs)", required = true) + Map<String, String> config); + + @GET + @Path("/{adjunct}") + @ApiOperation(value = "Gets detail of an adjunct") + @ApiResponses(value = { + @ApiResponse(code = 404, message = "Could not find application, entity or adjunct") + }) + public AdjunctDetail get( + @ApiParam(name = "application", value = "Application ID or name", required = true) + @PathParam("application") String application, + + @ApiParam(name = "entity", value = "Entity ID or name", required = true) + @PathParam("entity") String entityToken, + + @ApiParam(name = "adjunct", value = "Adjunct ID or name", required = true) + @PathParam("adjunct") String adjunctId); + + @GET + @Path("/{adjunct}/status") + @ApiOperation(value = "Gets status of an adjunct (RUNNING / SUSPENDED)") + @ApiResponses(value = { + @ApiResponse(code = 404, message = "Could not find application, entity or adjunct") + }) + public Status getStatus( + @ApiParam(name = "application", value = "Application ID or name", required = true) + @PathParam("application") String application, + + @ApiParam(name = "entity", value = "Entity ID or name", required = true) + @PathParam("entity") String entityToken, + + @ApiParam(name = "adjunct", value = "Adjunct ID or name", required = true) + @PathParam("adjunct") String adjunctId); + + @POST + @Path("/{adjunct}/start") + @ApiOperation(value = "Start or resume an adjunct") + @ApiResponses(value = { + @ApiResponse(code = 404, message = "Could not find application, entity or adjunct") + }) + public Response start( + @ApiParam(name = "application", value = "Application ID or name", required = true) + @PathParam("application") String application, + + @ApiParam(name = "entity", value = "Entity ID or name", required = true) + @PathParam("entity") String entityToken, + + @ApiParam(name = "adjunct", value = "Adjunct ID or name", required = true) + @PathParam("adjunct") String adjunctId); + + @POST + @Path("/{adjunct}/stop") + @ApiOperation(value = "Suspends an adjunct") + @ApiResponses(value = { + @ApiResponse(code = 404, message = "Could not find application, entity or adjunct") + }) + public Response stop( + @ApiParam(name = "application", value = "Application ID or name", required = true) + @PathParam("application") String application, + + @ApiParam(name = "entity", value = "Entity ID or name", required = true) + @PathParam("entity") String entityToken, + + @ApiParam(name = "adjunct", value = "Adjunct ID or name", required = true) + @PathParam("adjunct") String adjunctId); + + @DELETE + @Path("/{adjunct}") + @ApiOperation(value = "Destroy an adjunct", notes="Removes an adjunct from being associated with the entity and destroys it (stopping first if running)") + @ApiResponses(value = { + @ApiResponse(code = 404, message = "Could not find application, entity or adjunct") + }) + public Response destroy( + @ApiParam(name = "application", value = "Application ID or name", required = true) + @PathParam("application") String application, + + @ApiParam(name = "entity", value = "Entity ID or name", required = true) + @PathParam("entity") String entityToken, + + @ApiParam(name = "adjunct", value = "Adjunct ID or name", required = true) + @PathParam("adjunct") String adjunctId); + + /// ---------- config --------------- + + @GET + @Path("/{adjunct}/config") + @ApiOperation(value = "Fetch the config keys for a specific adjunct", + response = org.apache.brooklyn.rest.domain.AdjunctConfigSummary.class, + responseContainer = "List") + @ApiResponses(value = { + @ApiResponse(code = 404, message = "Could not find application or entity or adjunct") + }) + public List<AdjunctConfigSummary> listConfig( + @ApiParam(value = "Application ID or name", required = true) + @PathParam("application") final String application, + @ApiParam(value = "Entity ID or name", required = true) + @PathParam("entity") final String entityToken, + @ApiParam(value = "Adjunct ID or name", required = true) + @PathParam("adjunct") final String adjunctToken); + + // TODO support parameters ?show=value,summary&name=xxx &format={string,json,xml} + // (and in sensors class) + @GET + @Path("/{adjunct}/config-current") + @ApiOperation(value = "Fetch config key values in batch", notes="Returns a map of config name to value") + public Map<String, Object> batchConfigRead( + @ApiParam(value = "Application ID or name", required = true) + @PathParam("application") String application, + @ApiParam(value = "Entity ID or name", required = true) + @PathParam("entity") String entityToken, + @ApiParam(value = "Adjunct ID or name", required = true) + @PathParam("adjunct") String adjunctToken) ; + + @GET + @Path("/{adjunct}/config/{config}") + @ApiOperation(value = "Fetch config value", response = Object.class) + @ApiResponses(value = { + @ApiResponse(code = 404, message = "Could not find application, entity, adjunct or config key") + }) + public String getConfig( + @ApiParam(value = "Application ID or name", required = true) + @PathParam("application") String application, + @ApiParam(value = "Entity ID or name", required = true) + @PathParam("entity") String entityToken, + @ApiParam(value = "Adjunct ID or name", required = true) + @PathParam("adjunct") String adjunctToken, + @ApiParam(value = "Config key ID", required = true) + @PathParam("config") String configKeyName); + + @POST + @Path("/{adjunct}/config/{config}") + @Consumes(value = {"*/*"}) + @ApiOperation(value = "Sets the given config on this adjunct") + @ApiResponses(value = { + @ApiResponse(code = 404, message = "Could not find application, entity, adjunct or config key") + }) + public Response setConfig( + @ApiParam(value = "Application ID or name", required = true) + @PathParam("application") String application, + @ApiParam(value = "Entity ID or name", required = true) + @PathParam("entity") String entityToken, + @ApiParam(value = "Adjunct ID or name", required = true) + @PathParam("adjunct") String adjunctToken, + @ApiParam(value = "Config key ID", required = true) + @PathParam("config") String configKeyName, + @ApiParam(name = "value", value = "New value for the configuration", required = true) + Object value); +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/PolicyApi.java ---------------------------------------------------------------------- diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/PolicyApi.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/PolicyApi.java index a698f7d..329c0e1 100644 --- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/PolicyApi.java +++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/PolicyApi.java @@ -36,6 +36,8 @@ import java.util.Map; @Api("Entity Policies") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) +/** @deprecated since 0.12.0 use AdjunctApi */ +@Deprecated public interface PolicyApi { @GET http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/PolicyConfigApi.java ---------------------------------------------------------------------- diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/PolicyConfigApi.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/PolicyConfigApi.java index 1e6bfe6..06a4b86 100644 --- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/PolicyConfigApi.java +++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/PolicyConfigApi.java @@ -18,23 +18,32 @@ */ package org.apache.brooklyn.rest.api; -import io.swagger.annotations.Api; -import org.apache.brooklyn.rest.domain.PolicyConfigSummary; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiParam; +import java.util.List; +import java.util.Map; -import javax.ws.rs.*; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import java.util.List; -import java.util.Map; + +import org.apache.brooklyn.rest.domain.PolicyConfigSummary; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; @Path("/applications/{application}/entities/{entity}/policies/{policy}/config") @Api("Entity Policy Config") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) +/** @deprecated since 0.12.0 use AdjunctApi */ +@Deprecated public interface PolicyConfigApi { @GET http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/AdjunctDetail.java ---------------------------------------------------------------------- diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/AdjunctDetail.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/AdjunctDetail.java new file mode 100644 index 0000000..ddce5f4 --- /dev/null +++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/AdjunctDetail.java @@ -0,0 +1,72 @@ +/* + * 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.domain; + +import java.util.Map; +import java.util.Set; + +import org.apache.brooklyn.api.objs.EntityAdjunct; +import org.apache.brooklyn.util.collections.MutableMap; +import org.apache.brooklyn.util.collections.MutableSet; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; + +public class AdjunctDetail extends AdjunctSummary { + + private static final long serialVersionUID = -5086680835225136768L; + + @JsonInclude(Include.NON_EMPTY) + private String functionallyUniqueIdentifier; + @JsonInclude(Include.NON_EMPTY) + private Set<Object> tags; + @JsonInclude(Include.NON_EMPTY) + final Set<AdjunctConfigSummary> parameters = MutableSet.of(); + final Map<String,Object> config = MutableMap.of(); + + // for json + protected AdjunctDetail() {} + + public AdjunctDetail(EntityAdjunct a) { + super(a); + this.functionallyUniqueIdentifier = a.getUniqueTag(); + this.tags = a.tags().getTags(); + } + + public String getFunctionallyUniqueIdentifier() { + return functionallyUniqueIdentifier; + } + + public Set<Object> getTags() { + return tags; + } + + public AdjunctDetail parameter(AdjunctConfigSummary p) { + parameters.add(p); return this; + } + + public AdjunctDetail config(String key, Object val) { + config.put(key, val); return this; + } + + public AdjunctDetail config(Map<String,Object> vals) { + config.putAll(vals); return this; + } + +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/AdjunctSummary.java ---------------------------------------------------------------------- diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/AdjunctSummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/AdjunctSummary.java new file mode 100644 index 0000000..ca80644 --- /dev/null +++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/AdjunctSummary.java @@ -0,0 +1,150 @@ +/* + * 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.domain; + +import java.io.Serializable; +import java.net.URI; +import java.util.Map; +import java.util.Objects; + +import org.apache.brooklyn.api.objs.BrooklynObjectType; +import org.apache.brooklyn.api.objs.EntityAdjunct; +import org.apache.brooklyn.api.objs.HighlightTuple; +import org.apache.brooklyn.api.objs.Identifiable; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.google.common.collect.ImmutableMap; + +public class AdjunctSummary implements HasName, Serializable, Identifiable { + + private static final long serialVersionUID = -8106551648118942612L; + + private String id; + private String name; + private BrooklynObjectType adjunctType; + @JsonInclude(Include.NON_EMPTY) + private String catalogItemId; + private Status state; + @JsonInclude(Include.NON_EMPTY) + private Map<String, HighlightTuple> highlights; + + private Map<String, URI> links; + + // for json + protected AdjunctSummary() {} + + public AdjunctSummary(EntityAdjunct a) { + id = a.getId(); + name = a.getDisplayName(); + adjunctType = BrooklynObjectType.of(a); + catalogItemId = a.getCatalogItemId(); + highlights = a.getHighlights(); + } + + /** @deprecated since 0.12.0 only for legacy type-specific summary classes */ + @Deprecated + protected AdjunctSummary( + String id, + String name, + BrooklynObjectType adjunctType, + String catalogItemId, + Status state, + Map<String, HighlightTuple> highlights, + Map<String, URI> links) { + this.id = id; + this.name = name; + this.adjunctType = adjunctType; + this.catalogItemId = catalogItemId; + this.state = state; + this.highlights = (highlights == null) ? ImmutableMap.of() : ImmutableMap.copyOf(highlights); + this.links = (links == null) ? ImmutableMap.<String, URI> of() : ImmutableMap.copyOf(links); + } + + @Override + public String getId() { + return id; + } + + @Override + public String getName() { + return name; + } + + public BrooklynObjectType getAdjunctType() { + return adjunctType; + } + + public String getCatalogItemId() { + return catalogItemId; + } + + public Status getState() { + return state; + } + + public Map<String, HighlightTuple> getHighlights() { + return highlights; + } + + public Map<String, URI> getLinks() { + return links; + } + + public AdjunctSummary state(Status state) { + this.state = state; return this; + } + + public AdjunctSummary links(Map<String, URI> links) { + this.links = links; return this; + } + + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof AdjunctSummary)) return false; + AdjunctSummary that = (AdjunctSummary) o; + return Objects.equals(id, that.id) && + Objects.equals(name, that.name) && + Objects.equals(adjunctType, that.adjunctType) && + Objects.equals(catalogItemId, that.catalogItemId) && + Objects.equals(state, that.state) && + Objects.equals(highlights, that.highlights) && + Objects.equals(links, that.links) ; + } + + @Override + public int hashCode() { + return Objects.hash(id, name, adjunctType, catalogItemId, state, highlights, links); + } + + @Override + public String toString() { + return (adjunctType!=null ? adjunctType.name() : "AdjunctSummary")+"{" + + "id='" + id + '\'' + + ", name='" + name + '\'' + + ", catalogItemId='" + catalogItemId + '\'' + + ", state='" + state + '\'' + + ", highlights=" + highlights + + ", links=" + links + + '}'; + } + +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogEnricherSummary.java ---------------------------------------------------------------------- diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogEnricherSummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogEnricherSummary.java index 2e81ae1..c655515 100644 --- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogEnricherSummary.java +++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogEnricherSummary.java @@ -27,6 +27,8 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.google.common.collect.ImmutableSet; +/** @deprecated since 0.12.0 new TypeApi returns TypeSummary */ +@Deprecated public class CatalogEnricherSummary extends CatalogItemSummary { private static final long serialVersionUID = -588856488327394445L; http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogEntitySummary.java ---------------------------------------------------------------------- diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogEntitySummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogEntitySummary.java index 5947b7f..7dbc1be 100644 --- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogEntitySummary.java +++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogEntitySummary.java @@ -27,6 +27,8 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonSerialize; +/** @deprecated since 0.12.0 new TypeApi returns TypeSummary */ +@Deprecated public class CatalogEntitySummary extends CatalogItemSummary { private static final long serialVersionUID = 1063908984191424539L; http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogItemSummary.java ---------------------------------------------------------------------- diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogItemSummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogItemSummary.java index cdb86d4..cd85c13 100644 --- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogItemSummary.java +++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogItemSummary.java @@ -36,6 +36,8 @@ import com.google.common.collect.ImmutableMap; * see also, subclasses */ @JsonIgnoreProperties(ignoreUnknown = true) // ignore unknown, ie properties from subclasses (entity) +/** @deprecated since 0.12.0 new TypeApi returns TypeSummary */ +@Deprecated public class CatalogItemSummary implements HasId, HasName, Serializable { private static final long serialVersionUID = -823483595879417681L; http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogLocationSummary.java ---------------------------------------------------------------------- diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogLocationSummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogLocationSummary.java index 0213a0c..5fb1a4b 100644 --- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogLocationSummary.java +++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogLocationSummary.java @@ -26,6 +26,8 @@ import java.util.Set; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.ImmutableSet; +/** @deprecated since 0.12.0 new TypeApi returns TypeSummary */ +@Deprecated public class CatalogLocationSummary extends CatalogItemSummary { private static final long serialVersionUID = 8420991584336514673L; http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogPolicySummary.java ---------------------------------------------------------------------- diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogPolicySummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogPolicySummary.java index 37e5786..ad67786 100644 --- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogPolicySummary.java +++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogPolicySummary.java @@ -27,6 +27,8 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.google.common.collect.ImmutableSet; +/** @deprecated since 0.12.0 new TypeApi returns TypeSummary */ +@Deprecated public class CatalogPolicySummary extends CatalogItemSummary { private static final long serialVersionUID = -588856488327394445L; http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EnricherConfigSummary.java ---------------------------------------------------------------------- diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EnricherConfigSummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EnricherConfigSummary.java index c868fb8..7b40206 100644 --- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EnricherConfigSummary.java +++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EnricherConfigSummary.java @@ -23,7 +23,8 @@ import java.util.Map; import org.apache.brooklyn.config.ConfigKey; -// TODO remove? this class has no value over its super +/** @deprecated since 0.12.0 new {@link AdjunctConfigSummary}; this class does nothing additional */ +@Deprecated public class EnricherConfigSummary extends AdjunctConfigSummary { private static final long serialVersionUID = 4339330833863794513L; http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/PolicyConfigSummary.java ---------------------------------------------------------------------- diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/PolicyConfigSummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/PolicyConfigSummary.java index 5dfb898..797bd0c 100644 --- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/PolicyConfigSummary.java +++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/PolicyConfigSummary.java @@ -23,7 +23,8 @@ import java.util.Map; import org.apache.brooklyn.config.ConfigKey; -//TODO remove? this class has no value over its super +/** @deprecated since 0.12.0 new {@link AdjunctConfigSummary}; this class does nothing additional */ +@Deprecated public class PolicyConfigSummary extends AdjunctConfigSummary { private static final long serialVersionUID = 4339330833863794513L; http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/PolicySummary.java ---------------------------------------------------------------------- diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/PolicySummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/PolicySummary.java index ce9c9e0..da4a86a 100644 --- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/PolicySummary.java +++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/PolicySummary.java @@ -18,29 +18,20 @@ */ package org.apache.brooklyn.rest.domain; -import java.io.Serializable; import java.net.URI; import java.util.Map; -import java.util.Objects; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.google.common.collect.ImmutableMap; +import org.apache.brooklyn.api.objs.BrooklynObjectType; import org.apache.brooklyn.api.objs.HighlightTuple; -public class PolicySummary implements HasName, HasId, Serializable { +import com.fasterxml.jackson.annotation.JsonProperty; + +/** @deprecated since 0.12.0 use {@link AdjunctSummary}; this class is identical */ +@Deprecated +public class PolicySummary extends AdjunctSummary { private static final long serialVersionUID = -5086680835225136768L; - private final String id; - private final String name; - @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) - private final String catalogItemId; - private final Status state; - private final Map<String, URI> links; - private final Map<String, HighlightTuple> highlights; - public PolicySummary( @JsonProperty("id") String id, @JsonProperty("name") String name, @@ -48,67 +39,7 @@ public class PolicySummary implements HasName, HasId, Serializable { @JsonProperty("state") Status state, @JsonProperty("highlights") Map<String, HighlightTuple> highlights, @JsonProperty("links") Map<String, URI> links) { - this.id = id; - this.name = name; - this.catalogItemId = catalogItemId; - this.state = state; - this.links = (links == null) ? ImmutableMap.<String, URI> of() : ImmutableMap.copyOf(links); - this.highlights = (highlights == null) ? ImmutableMap.of() : ImmutableMap.copyOf(highlights); - } - - @Override - public String getId() { - return id; - } - - @Override - public String getName() { - return name; - } - - public String getCatalogItemId() { - return catalogItemId; + super(id, name, BrooklynObjectType.POLICY, catalogItemId, state, highlights, links); } - public Status getState() { - return state; - } - - public Map<String, URI> getLinks() { - return links; - } - - public Map<String, HighlightTuple> getHighlights() { - return highlights; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof PolicySummary)) return false; - PolicySummary that = (PolicySummary) o; - return Objects.equals(id, that.id) && - Objects.equals(name, that.name) && - Objects.equals(catalogItemId, that.catalogItemId) && - state == that.state && - Objects.equals(highlights, that.highlights) && - Objects.equals(links, that.links) ; - } - - @Override - public int hashCode() { - return Objects.hash(id, name, catalogItemId, state, highlights, links); - } - - @Override - public String toString() { - return "PolicySummary{" + - "id='" + id + '\'' + - ", name='" + name + '\'' + - ", catalogItemId='" + catalogItemId + '\'' + - ", state=" + state + - ", highlights=" + highlights + - ", links=" + links + - '}'; - } } http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/Status.java ---------------------------------------------------------------------- diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/Status.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/Status.java index e2b2ce4..4a998c7 100644 --- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/Status.java +++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/Status.java @@ -18,9 +18,10 @@ */ package org.apache.brooklyn.rest.domain; -/** - * @author Adam Lowe - */ +import org.apache.brooklyn.core.entity.lifecycle.Lifecycle; + +/** Canonical set, similar to {@link Lifecycle}, but cleaned up for outside consumption + * and more appropriate for adjunct types */ public enum Status { ACCEPTED, STARTING, http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/BrooklynRestApi.java ---------------------------------------------------------------------- diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/BrooklynRestApi.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/BrooklynRestApi.java index b3eb4e4..233d717 100644 --- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/BrooklynRestApi.java +++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/BrooklynRestApi.java @@ -24,6 +24,7 @@ import java.util.List; import org.apache.brooklyn.rest.resources.AbstractBrooklynRestResource; import org.apache.brooklyn.rest.resources.AccessResource; import org.apache.brooklyn.rest.resources.ActivityResource; +import org.apache.brooklyn.rest.resources.AdjunctResource; import org.apache.brooklyn.rest.resources.ApidocResource; import org.apache.brooklyn.rest.resources.ApplicationResource; import org.apache.brooklyn.rest.resources.BundleResource; @@ -63,6 +64,7 @@ public class BrooklynRestApi { resources.add(new EntityConfigResource()); resources.add(new SensorResource()); resources.add(new EffectorResource()); + resources.add(new AdjunctResource()); resources.add(new PolicyResource()); resources.add(new PolicyConfigResource()); resources.add(new ActivityResource()); http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/AdjunctResource.java ---------------------------------------------------------------------- diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/AdjunctResource.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/AdjunctResource.java new file mode 100644 index 0000000..9dd4c10 --- /dev/null +++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/AdjunctResource.java @@ -0,0 +1,263 @@ +/* + * 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.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.ws.rs.core.Context; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.api.internal.AbstractBrooklynObjectSpec; +import org.apache.brooklyn.api.objs.BrooklynObjectType; +import org.apache.brooklyn.api.objs.EntityAdjunct; +import org.apache.brooklyn.api.policy.Policy; +import org.apache.brooklyn.api.policy.PolicySpec; +import org.apache.brooklyn.api.sensor.Enricher; +import org.apache.brooklyn.api.sensor.EnricherSpec; +import org.apache.brooklyn.api.sensor.Feed; +import org.apache.brooklyn.api.typereg.RegisteredType; +import org.apache.brooklyn.config.ConfigKey; +import org.apache.brooklyn.core.config.ConfigPredicates; +import org.apache.brooklyn.core.entity.EntityInternal; +import org.apache.brooklyn.core.mgmt.entitlement.Entitlements; +import org.apache.brooklyn.rest.api.AdjunctApi; +import org.apache.brooklyn.rest.domain.AdjunctConfigSummary; +import org.apache.brooklyn.rest.domain.AdjunctDetail; +import org.apache.brooklyn.rest.domain.AdjunctSummary; +import org.apache.brooklyn.rest.domain.Status; +import org.apache.brooklyn.rest.domain.SummaryComparators; +import org.apache.brooklyn.rest.filter.HaHotStateRequired; +import org.apache.brooklyn.rest.transform.AdjunctTransformer; +import org.apache.brooklyn.rest.transform.EntityTransformer; +import org.apache.brooklyn.rest.util.BrooklynRestResourceUtils; +import org.apache.brooklyn.rest.util.WebResourceUtils; +import org.apache.brooklyn.util.core.ClassLoaderUtils; +import org.apache.brooklyn.util.core.flags.TypeCoercions; +import org.apache.brooklyn.util.exceptions.Exceptions; +import org.apache.brooklyn.util.text.Strings; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Function; +import com.google.common.base.Predicates; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +@HaHotStateRequired +public class AdjunctResource extends AbstractBrooklynRestResource implements AdjunctApi { + + private static final Logger log = LoggerFactory.getLogger(AdjunctResource.class); + + private @Context UriInfo ui; + + @Override + public List<AdjunctSummary> list(String application, String entityToken, String adjunctType) { + final Entity entity = brooklyn().getEntity(application, entityToken); + Iterable<? extends EntityAdjunct> source = Collections.emptyList(); + boolean all = Strings.isBlank(adjunctType); + boolean any = false; + if (all || adjunctType.equalsIgnoreCase(BrooklynObjectType.POLICY.name())) { + any = true; + source = Iterables.concat(source, entity.policies()); + } + if (all || adjunctType.equalsIgnoreCase(BrooklynObjectType.ENRICHER.name())) { + any = true; + source = Iterables.concat(source, entity.enrichers()); + } + if (all || adjunctType.equalsIgnoreCase(BrooklynObjectType.FEED.name())) { + any = true; + source = Iterables.concat(source, ((EntityInternal)entity).feeds()); + } + if (!any) { + throw WebResourceUtils.badRequest("Unknown adjunct type '%s'; use 'policy', 'enricher', or 'feed'", adjunctType); + } + return FluentIterable.from(source) + .transform(new Function<EntityAdjunct, AdjunctSummary>() { + @Override + public AdjunctSummary apply(EntityAdjunct adjunct) { + return AdjunctTransformer.adjunctSummary(entity, adjunct, ui.getBaseUriBuilder()); + } + }) + .toSortedList(SummaryComparators.nameComparator()); + } + + // TODO would like to make 'config' arg optional but jersey complains if we do + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + public AdjunctSummary addAdjunct(String application, String entityToken, String adjunctTypeName, Map<String, String> config) { + Entity entity = brooklyn().getEntity(application, entityToken); + if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.MODIFY_ENTITY, entity)) { + throw WebResourceUtils.forbidden("User '%s' is not authorized to modify entity '%s'", + Entitlements.getEntitlementContext().user(), entity); + } + + RegisteredType rt = brooklyn().getTypeRegistry().get(adjunctTypeName); + AbstractBrooklynObjectSpec<?, ?> spec; + if (rt!=null) { + spec = brooklyn().getTypeRegistry().createSpec(rt, null, null); + } else { + try { + Class<?> type = new ClassLoaderUtils(this, mgmt()).loadClass(adjunctTypeName); + if (Policy.class.isAssignableFrom(type)) spec = PolicySpec.create((Class) type); + else if (Enricher.class.isAssignableFrom(type)) spec = EnricherSpec.create((Class) type); + else if (Feed.class.isAssignableFrom(type)) { + // TODO add FeedSpec ? would be needed even if using the type registry + throw WebResourceUtils.badRequest("Creation of feeds from java type (%s) not supported", adjunctTypeName); + } else { + throw WebResourceUtils.badRequest("Invalid type %s; not a support adjunct type", adjunctTypeName); + } + } catch (ClassNotFoundException e) { + throw WebResourceUtils.badRequest("No adjunct with type %s found", adjunctTypeName); + } catch (ClassCastException e) { + throw WebResourceUtils.badRequest("No adjunct with type %s found", adjunctTypeName); + } catch (Exception e) { + throw Exceptions.propagate(e); + } + } + + spec.configure(config); + + EntityAdjunct instance; + if (spec instanceof PolicySpec) instance = entity.policies().add((PolicySpec)spec); + else if (spec instanceof EnricherSpec) instance = entity.enrichers().add((EnricherSpec)spec); + else { + // TODO add FeedSpec + throw WebResourceUtils.badRequest("Unexpected spec type %s", spec); + } + + log.debug("REST API added adjunct " + instance + " to " + entity); + + return AdjunctTransformer.adjunctDetail(brooklyn(), entity, instance, ui.getBaseUriBuilder()); + } + + @Override + public AdjunctDetail get(String application, String entityToken, String adjunctId) { + Entity entity = brooklyn().getEntity(application, entityToken); + EntityAdjunct adjunct = brooklyn().getAdjunct(entity, adjunctId); + + return AdjunctTransformer.adjunctDetail(brooklyn(), entity, adjunct, ui.getBaseUriBuilder()); + } + + @Override + public Status getStatus(String application, String entityToken, String adjunctId) { + return AdjunctTransformer.inferStatus( brooklyn().getAdjunct(application, entityToken, adjunctId) ); + } + + @Override + public Response start(String application, String entityToken, String adjunctId) { + EntityAdjunct adjunct = brooklyn().getAdjunct(application, entityToken, adjunctId); + if (adjunct instanceof Policy) ((Policy)adjunct).resume(); + else if (adjunct instanceof Feed) ((Feed)adjunct).resume(); + else throw WebResourceUtils.badRequest("%s does not support start/resume", adjunct); + + return Response.status(Response.Status.NO_CONTENT).build(); + } + + @Override + public Response stop(String application, String entityToken, String adjunctId) { + EntityAdjunct adjunct = brooklyn().getAdjunct(application, entityToken, adjunctId); + if (adjunct instanceof Policy) ((Policy)adjunct).suspend(); + else if (adjunct instanceof Feed) ((Feed)adjunct).suspend(); + else throw WebResourceUtils.badRequest("%s does not support suspend", adjunct); + + return Response.status(Response.Status.NO_CONTENT).build(); + } + + @Override + public Response destroy(String application, String entityToken, String adjunctId) { + Entity entity = brooklyn().getEntity(application, entityToken); + EntityAdjunct adjunct = brooklyn().getAdjunct(entity, adjunctId); + + if (adjunct instanceof Policy) { + ((Policy)adjunct).suspend(); + entity.policies().remove((Policy) adjunct); + } else if (adjunct instanceof Enricher) { + entity.enrichers().remove((Enricher) adjunct); + } else if (adjunct instanceof Feed) { + ((Feed)adjunct).suspend(); + ((EntityInternal)entity).feeds().remove((Feed) adjunct); + } else { + throw WebResourceUtils.badRequest("Unexpected adjunct type %s", adjunct); + } + + return Response.status(Response.Status.NO_CONTENT).build(); + } + + // ---- config ---- + + @Override + public List<AdjunctConfigSummary> listConfig( + final String application, final String entityToken, final String adjunctToken) { + Entity entity = brooklyn().getEntity(application, entityToken); + EntityAdjunct adjunct = brooklyn().getAdjunct(entity, adjunctToken); + + List<AdjunctConfigSummary> result = Lists.newArrayList(); + for (ConfigKey<?> key : adjunct.config().findKeysPresent(Predicates.alwaysTrue())) { + result.add(AdjunctTransformer.configSummary(brooklyn(), entity, adjunct, key, ui.getBaseUriBuilder())); + } + 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 adjunctToken) { + // TODO: add test + return EntityTransformer.getConfigValues(brooklyn(), brooklyn().getAdjunct(application, entityToken, adjunctToken) ); + } + + @Override + public String getConfig(String application, String entityToken, String adjunctToken, String configKeyName) { + EntityAdjunct adjunct = brooklyn().getAdjunct(application, entityToken, adjunctToken); + Set<ConfigKey<?>> cki = adjunct.config().findKeysDeclared(ConfigPredicates.nameSatisfies(Predicates.equalTo(configKeyName))); + if (cki.isEmpty()) throw WebResourceUtils.notFound("Cannot find config key '%s' in policy '%s' of entity '%s'", configKeyName, adjunctToken, entityToken); + + return brooklyn().getStringValueForDisplay(adjunct.config().get(cki.iterator().next())); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + public Response setConfig(String application, String entityToken, String adjunctToken, String configKeyName, Object value) { + Entity entity = brooklyn().getEntity(application, entityToken); + if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.MODIFY_ENTITY, entity)) { + throw WebResourceUtils.forbidden("User '%s' is not authorized to modify entity '%s'", + Entitlements.getEntitlementContext().user(), entity); + } + + EntityAdjunct adjunct = brooklyn().getAdjunct(entity, adjunctToken); + Set<ConfigKey<?>> cki = adjunct.config().findKeysDeclared(ConfigPredicates.nameSatisfies(Predicates.equalTo(configKeyName))); + if (cki.isEmpty()) throw WebResourceUtils.notFound("Cannot find config key '%s' in policy '%s' of entity '%s'", configKeyName, adjunctToken, entityToken); + ConfigKey<?> ck = cki.iterator().next(); + + adjunct.config().set((ConfigKey) cki, TypeCoercions.coerce(value, ck.getTypeToken())); + + return Response.status(Response.Status.OK).build(); + } + + public static String getStringValueForDisplay(BrooklynRestResourceUtils utils, EntityAdjunct policy, Object value) { + return utils.getStringValueForDisplay(value); + } + +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/PolicyConfigResource.java ---------------------------------------------------------------------- diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/PolicyConfigResource.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/PolicyConfigResource.java index 6dcdf19..1fca535 100644 --- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/PolicyConfigResource.java +++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/PolicyConfigResource.java @@ -41,6 +41,7 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; @HaHotStateRequired +@Deprecated public class PolicyConfigResource extends AbstractBrooklynRestResource implements PolicyConfigApi { @Override http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/PolicyResource.java ---------------------------------------------------------------------- diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/PolicyResource.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/PolicyResource.java index 0b8d2c2..125b0b7 100644 --- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/PolicyResource.java +++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/PolicyResource.java @@ -47,6 +47,7 @@ import com.google.common.collect.FluentIterable; import com.google.common.collect.Maps; @HaHotStateRequired +@Deprecated public class PolicyResource extends AbstractBrooklynRestResource implements PolicyApi { private static final Logger log = LoggerFactory.getLogger(PolicyResource.class); http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/AdjunctTransformer.java ---------------------------------------------------------------------- diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/AdjunctTransformer.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/AdjunctTransformer.java new file mode 100644 index 0000000..db7d4cd --- /dev/null +++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/AdjunctTransformer.java @@ -0,0 +1,117 @@ +/* + * 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.transform; + +import static org.apache.brooklyn.rest.util.WebResourceUtils.serviceUriBuilder; + +import java.net.URI; +import java.util.Map; + +import javax.ws.rs.core.UriBuilder; + +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.api.objs.EntityAdjunct; +import org.apache.brooklyn.api.policy.Policy; +import org.apache.brooklyn.api.sensor.Feed; +import org.apache.brooklyn.config.ConfigKey; +import org.apache.brooklyn.core.policy.Policies; +import org.apache.brooklyn.rest.api.AdjunctApi; +import org.apache.brooklyn.rest.api.ApplicationApi; +import org.apache.brooklyn.rest.api.EntityApi; +import org.apache.brooklyn.rest.domain.AdjunctConfigSummary; +import org.apache.brooklyn.rest.domain.AdjunctDetail; +import org.apache.brooklyn.rest.domain.AdjunctSummary; +import org.apache.brooklyn.rest.domain.ApplicationSummary; +import org.apache.brooklyn.rest.domain.Status; +import org.apache.brooklyn.rest.util.BrooklynRestResourceUtils; +import org.apache.brooklyn.util.collections.MutableMap; + +import com.google.common.base.Predicates; +import com.google.common.collect.ImmutableMap; + +/** + * Converts from Brooklyn entities to restful API summary objects + */ +public class AdjunctTransformer { + + public static AdjunctSummary adjunctSummary(Entity entity, EntityAdjunct adjunct, UriBuilder ub) { + return embellish(new AdjunctSummary(adjunct), entity, adjunct, ub); + } + + @SuppressWarnings("unchecked") + private static <T extends AdjunctSummary> T embellish(T adjunctSummary, Entity entity, EntityAdjunct adjunct, UriBuilder ub) { + return (T) adjunctSummary.state(inferStatus(adjunct)).links( buildLinks(entity, adjunct, ub, adjunctSummary instanceof AdjunctDetail) ); + } + + public static AdjunctDetail adjunctDetail(BrooklynRestResourceUtils utils, Entity entity, EntityAdjunct adjunct, UriBuilder ub) { + AdjunctDetail result = embellish(new AdjunctDetail(adjunct), entity, adjunct, ub); + for (ConfigKey<?> key: adjunct.config().findKeysDeclared(Predicates.alwaysTrue())) { + result.parameter(configSummary(utils, entity, adjunct, key, ub)); + } + result.config(EntityTransformer.getConfigValues(utils, adjunct)); + return result; + } + + protected static Map<String, URI> buildLinks(Entity entity, EntityAdjunct adjunct, UriBuilder ub, boolean detail) { + MutableMap<String,URI> links = MutableMap.of(); + + links.put("self", serviceUriBuilder(ub, AdjunctApi.class, "get").build(entity.getApplicationId(), entity.getId(), adjunct.getId())); + + if (detail) { + links.put("application", serviceUriBuilder(ub, ApplicationApi.class, "get").build(entity.getApplicationId())); + links.put("entity", serviceUriBuilder(ub, EntityApi.class, "get").build(entity.getApplicationId(), entity.getId())); + links.put("config", serviceUriBuilder(ub, AdjunctApi.class, "listConfig").build(entity.getApplicationId(), entity.getId(), adjunct.getId())); + links.put("status", serviceUriBuilder(ub, AdjunctApi.class, "getStatus").build(entity.getApplicationId(), entity.getId(), adjunct.getId())); + if (adjunct instanceof Policy || adjunct instanceof Feed) { + links.put("start", serviceUriBuilder(ub, AdjunctApi.class, "start").build(entity.getApplicationId(), entity.getId(), adjunct.getId())); + links.put("stop", serviceUriBuilder(ub, AdjunctApi.class, "stop").build(entity.getApplicationId(), entity.getId(), adjunct.getId())); + } + links.put("destroy", serviceUriBuilder(ub, AdjunctApi.class, "destroy").build(entity.getApplicationId(), entity.getId(), adjunct.getId())); + } + + return links.asUnmodifiable(); + } + + public static Status inferStatus(EntityAdjunct adjunct) { + return ApplicationTransformer.statusFromLifecycle( Policies.inferAdjunctStatus(adjunct) ); + } + + public static AdjunctConfigSummary configSummary(BrooklynRestResourceUtils utils, ApplicationSummary application, Entity entity, EntityAdjunct adjunct, ConfigKey<?> config, UriBuilder ub) { + return configSummary(utils, entity, adjunct, config, ub); + } + + public static AdjunctConfigSummary configSummary(BrooklynRestResourceUtils utils, Entity entity, EntityAdjunct adjunct, ConfigKey<?> config, UriBuilder ub) { + URI applicationUri = serviceUriBuilder(ub, ApplicationApi.class, "get").build(entity.getApplicationId()); + URI entityUri = serviceUriBuilder(ub, EntityApi.class, "get").build(entity.getApplicationId(), entity.getId()); + URI adjunctUri = serviceUriBuilder(ub, AdjunctApi.class, "get").build(entity.getApplicationId(), entity.getId(), adjunct.getId()); + URI configUri = serviceUriBuilder(ub, AdjunctApi.class, "getConfig").build(entity.getApplicationId(), entity.getId(), adjunct.getId(), config.getName()); + + Map<String, URI> links = ImmutableMap.<String, URI>builder() + .put("self", configUri) + .put("application", applicationUri) + .put("entity", entityUri) + .put("policy", adjunctUri) + .build(); + + return new AdjunctConfigSummary(config.getName(), config.getTypeName(), config.getDescription(), + utils.getStringValueForDisplay(config.getDefaultValue()), + config.isReconfigurable(), + links); + } +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/EntityTransformer.java ---------------------------------------------------------------------- diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/EntityTransformer.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/EntityTransformer.java index 6bf3f19..cb7877f 100644 --- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/EntityTransformer.java +++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/EntityTransformer.java @@ -32,10 +32,12 @@ import javax.ws.rs.core.UriBuilder; import org.apache.brooklyn.api.catalog.CatalogConfig; import org.apache.brooklyn.api.entity.Application; import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.api.objs.BrooklynObject; import org.apache.brooklyn.api.objs.SpecParameter; import org.apache.brooklyn.config.ConfigKey; import org.apache.brooklyn.core.catalog.internal.CatalogUtils; import org.apache.brooklyn.core.config.render.RendererHints; +import org.apache.brooklyn.core.objs.BrooklynObjectInternal; import org.apache.brooklyn.core.typereg.RegisteredTypes; import org.apache.brooklyn.rest.api.ApplicationApi; import org.apache.brooklyn.rest.api.CatalogApi; @@ -46,12 +48,15 @@ import org.apache.brooklyn.rest.domain.EnricherConfigSummary; import org.apache.brooklyn.rest.domain.EntityConfigSummary; import org.apache.brooklyn.rest.domain.EntitySummary; import org.apache.brooklyn.rest.domain.PolicyConfigSummary; +import org.apache.brooklyn.rest.util.BrooklynRestResourceUtils; import org.apache.brooklyn.util.collections.MutableMap; +import org.apache.brooklyn.util.core.config.ConfigBag; import com.google.common.base.Function; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; /** * @author Adam Lowe @@ -84,6 +89,7 @@ public class EntityTransformer { .put("config", URI.create(entityUri + "/config")) .put("sensors", URI.create(entityUri + "/sensors")) .put("effectors", URI.create(entityUri + "/effectors")) + .put("adjuncts", URI.create(entityUri + "/adjuncts")) .put("policies", URI.create(entityUri + "/policies")) .put("activities", URI.create(entityUri + "/activities")) .put("locations", URI.create(entityUri + "/locations")) @@ -212,4 +218,19 @@ public class EntityTransformer { Double priority = input.isPinned() ? Double.valueOf(1d) : null; return enricherConfigSummary(input.getConfigKey(), input.getLabel(), priority, null); } + + public static Map<String, Object> getConfigValues(BrooklynRestResourceUtils utils, BrooklynObject obj) { + // alternatively could do this - should be the same ? +// for (ConfigKey<?> key: adjunct.config().findKeysPresent(Predicates.alwaysTrue())) { +// result.config(key.getName(), utils.getStringValueForDisplay( adjunct.config().get(key) )); +// } + + Map<String, Object> source = ConfigBag.newInstance( + ((BrooklynObjectInternal)obj).config().getInternalConfigMap().getAllConfigInheritedRawValuesIgnoringErrors() ).getAllConfig(); + Map<String, Object> result = Maps.newLinkedHashMap(); + for (Map.Entry<String, Object> ek : source.entrySet()) { + result.put(ek.getKey(), utils.getStringValueForDisplay(ek.getValue())); + } + return result; + } } http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/PolicyTransformer.java ---------------------------------------------------------------------- diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/PolicyTransformer.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/PolicyTransformer.java index 8dad949..294fd41 100644 --- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/PolicyTransformer.java +++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/PolicyTransformer.java @@ -25,6 +25,7 @@ import org.apache.brooklyn.api.entity.Entity; import org.apache.brooklyn.api.policy.Policy; import org.apache.brooklyn.config.ConfigKey; import org.apache.brooklyn.core.policy.Policies; +import org.apache.brooklyn.rest.domain.AdjunctSummary; import org.apache.brooklyn.rest.domain.ApplicationSummary; import org.apache.brooklyn.rest.domain.PolicyConfigSummary; import org.apache.brooklyn.rest.domain.PolicySummary; @@ -42,7 +43,9 @@ import static org.apache.brooklyn.rest.util.WebResourceUtils.serviceUriBuilder; /** * Converts from Brooklyn entities to restful API summary objects + * @deprecated since 0.12.0 use {@link AdjunctTransformer} and {@link AdjunctSummary} */ +@Deprecated public class PolicyTransformer { public static PolicySummary policySummary(Entity entity, Policy policy, UriBuilder ub) { http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtils.java ---------------------------------------------------------------------- diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtils.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtils.java index 9e1ce53..9b0a3d4 100644 --- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtils.java +++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtils.java @@ -38,7 +38,10 @@ import org.apache.brooklyn.api.location.Location; import org.apache.brooklyn.api.location.LocationRegistry; import org.apache.brooklyn.api.mgmt.ManagementContext; import org.apache.brooklyn.api.mgmt.Task; +import org.apache.brooklyn.api.objs.EntityAdjunct; import org.apache.brooklyn.api.policy.Policy; +import org.apache.brooklyn.api.sensor.Enricher; +import org.apache.brooklyn.api.sensor.Feed; import org.apache.brooklyn.api.typereg.BrooklynTypeRegistry; import org.apache.brooklyn.api.typereg.RegisteredType; import org.apache.brooklyn.camp.brooklyn.BrooklynCampConstants; @@ -112,6 +115,16 @@ public class BrooklynRestResourceUtils { public Policy getPolicy(String application, String entity, String policy) { return getPolicy(getEntity(application, entity), policy); } + + /** finds the policy indicated by the given ID or name. + * @see {@link #getEntity(String,String)}; it then searches the policies of that + * entity for one whose ID or name matches that given. + * <p> + * + * @throws 404 or 412 (unless input is null in which case output is null) */ + public EntityAdjunct getAdjunct(String application, String entity, String adjunct) { + return getAdjunct(getEntity(application, entity), adjunct); + } /** finds the policy indicated by the given ID or name. * @see {@link #getPolicy(String,String,String)}. @@ -130,6 +143,36 @@ public class BrooklynRestResourceUtils { throw WebResourceUtils.notFound("Cannot find policy '%s' in entity '%s'", policy, entity); } + + /** finds the policy indicated by the given ID or name. + * @see {@link #getAdjunct(String,String,String)}. + * <p> + * + * @throws 404 or 412 (unless input is null in which case output is null) */ + public EntityAdjunct getAdjunct(Entity entity, String adjunct) { + if (adjunct==null) return null; + + for (Policy p: entity.policies()) { + if (adjunct.equals(p.getId())) return p; + } + for (Policy p: entity.policies()) { + if (adjunct.equals(p.getDisplayName())) return p; + } + for (Enricher p: entity.enrichers()) { + if (adjunct.equals(p.getId())) return p; + } + for (Enricher p: entity.enrichers()) { + if (adjunct.equals(p.getDisplayName())) return p; + } + for (Feed p: ((EntityInternal)entity).feeds()) { + if (adjunct.equals(p.getId())) return p; + } + for (Feed p: ((EntityInternal)entity).feeds()) { + if (adjunct.equals(p.getDisplayName())) return p; + } + + throw WebResourceUtils.notFound("Cannot find adjunct '%s' in entity '%s'", adjunct, entity); + } /** finds the entity indicated by the given ID or name * <p>
