diff --git a/api/src/main/java/org/apache/brooklyn/api/entity/Entity.java b/api/src/main/java/org/apache/brooklyn/api/entity/Entity.java index da9169ec78..bbee0ade6c 100644 --- a/api/src/main/java/org/apache/brooklyn/api/entity/Entity.java +++ b/api/src/main/java/org/apache/brooklyn/api/entity/Entity.java @@ -286,7 +286,10 @@ public interface Entity extends BrooklynObject { /** * Adds the given feed to this entity. Also calls feed.setEntity if available. + * + * @deprecated since 0.13.0; see {@link FeedSupport#add(Feed)} */ + @Deprecated T addFeed(T feed); SensorSupport sensors(); 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 d50e09271d..01a47148fb 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(); 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 9f9dc05d93..bd7df0698b 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 @@ -1871,7 +1871,8 @@ public boolean removeAllEnrichers() { */ @Override public T addFeed(T feed) { - return feeds().add(feed); + feeds().add(feed); + return feed; } @Override @@ -1896,8 +1897,9 @@ public Collection getFeeds() { public T add(T feed) { return addFeed(feed); } - - @Override + + /** @deprecated since 0.13.0 use {@link #add(Feed)} */ + @Deprecated public T addFeed(T feed) { Feed old = findApparentlyEqualAndWarnIfNotSameUniqueTag(feedsInternal, feed); if (old != null) { @@ -1921,7 +1923,7 @@ public T addFeed(T feed) { getManagementContext().getRebindManager().getChangeListener().onManaged(feed); getManagementSupport().getEntityChangeListener().onFeedAdded(feed); // TODO Could add equivalent of AbstractEntity.POLICY_ADDED for feeds; no use-case for that yet - + return feed; } @@ -1930,7 +1932,8 @@ public boolean remove(Feed feed) { return removeFeed(feed); } - @Override + /** @deprecated since 0.13.0 use {@link #remove(Feed)} */ + @Deprecated public boolean removeFeed(Feed feed) { feed.stop(); boolean changed = feedsInternal.remove(feed); @@ -1955,6 +1958,27 @@ public boolean removeAllFeeds() { } return changed; } + + @Override + public Iterator iterator() { + return getFeeds().iterator(); + } + + // TODO add these back when we implement AdjunctSupport (after 0.13.0) +// @Override +// public int size() { +// return getFeeds().size(); +// } +// +// @Override +// public boolean isEmpty() { +// return getFeeds().isEmpty(); +// } +// +// @Override +// public List asList() { +// return ImmutableList.copyOf(getFeeds()); +// } } // -------- SENSORS -------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/EntityAdjuncts.java b/core/src/main/java/org/apache/brooklyn/core/entity/EntityAdjuncts.java index fc1daf45e2..8c06384da3 100644 --- a/core/src/main/java/org/apache/brooklyn/core/entity/EntityAdjuncts.java +++ b/core/src/main/java/org/apache/brooklyn/core/entity/EntityAdjuncts.java @@ -23,13 +23,17 @@ 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.Enricher; +import org.apache.brooklyn.api.sensor.Feed; +import org.apache.brooklyn.core.entity.lifecycle.Lifecycle; import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic.ComputeServiceIndicatorsFromChildrenAndMembers; import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic.ComputeServiceState; import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic.ServiceNotUpLogic; import org.apache.brooklyn.util.collections.MutableList; import org.apache.brooklyn.util.guava.Maybe; +import com.google.common.annotations.Beta; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; @@ -66,5 +70,27 @@ public static boolean isSystemEnricher(Enricher enr) { if (SYSTEM_ENRICHER_UNIQUE_TAGS.contains(enr.getUniqueTag())) return true; return false; } - + + @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; + } + + // Enricher doesn't support suspend so if not running or destroyed then + // it is just created + return Lifecycle.CREATED; + } + } 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 d6d51a1da4..46d10aba79 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,29 +189,27 @@ public interface SensorSupportInternal extends Entity.SensorSupport { void remove(AttributeSensor attribute); } - public interface FeedSupport { + // TODO extend AdjunctSupport, after 0.13.0 + public interface FeedSupport extends Iterable { Collection getFeeds(); /** * Adds the given feed to this entity. The feed will automatically be re-added on brooklyn restart. + * + * @deprecated since 0.13.0 will change to return type 'void', for consistency with other {@link AdjunctSupport} */ - T add(T feed); - - /** @deprecated since 0.10.0; use {@link #add()} */ @Deprecated - T addFeed(T feed); +// @Override + T add(T feed); /** * Removes the given feed from this entity. * @return True if the feed existed at this entity; false otherwise */ +// @Override boolean remove(Feed feed); - /** @deprecated since 0.10.0; use {@link #remove()} */ - @Deprecated - boolean removeFeed(Feed feed); - /** * Removes all feeds from this entity. * Use with caution as some entities automatically register feeds; this will remove those feeds as well. diff --git a/core/src/main/java/org/apache/brooklyn/core/feed/AbstractFeed.java b/core/src/main/java/org/apache/brooklyn/core/feed/AbstractFeed.java index 84165eae17..316d269597 100644 --- a/core/src/main/java/org/apache/brooklyn/core/feed/AbstractFeed.java +++ b/core/src/main/java/org/apache/brooklyn/core/feed/AbstractFeed.java @@ -62,7 +62,7 @@ public AbstractFeed() { public void setEntity(EntityLocal entity) { super.setEntity(entity); if (BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_FEED_REGISTRATION_PROPERTY)) { - ((EntityInternal)entity).feeds().addFeed(this); + ((EntityInternal)entity).feeds().add(this); } } 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 db9bece948..0af5c6a18d 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 @@ -18,74 +18,18 @@ */ 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.policy.Policy; -import org.apache.brooklyn.api.sensor.Sensor; -import org.apache.brooklyn.api.sensor.SensorEvent; -import org.apache.brooklyn.api.sensor.SensorEventListener; +import org.apache.brooklyn.core.entity.EntityAdjuncts; import org.apache.brooklyn.core.entity.lifecycle.Lifecycle; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import groovy.lang.Closure; - -@SuppressWarnings({"rawtypes","unchecked"}) +/** @deprecated since 0.13.0 use {@link EntityAdjuncts} */ +@Deprecated public class Policies { - private static final Logger LOG = LoggerFactory.getLogger(Policies.class); - - /** - * @deprecated since 0.11.0; explicit groovy utilities/support will be deleted. - */ - @Deprecated - public static SensorEventListener listenerFromValueClosure(final Closure code) { - LOG.warn("Use of groovy.lang.Closure is deprecated in Policies.listenerFromValueClosure()"); - return new SensorEventListener() { - @Override - public void onEvent(SensorEvent event) { - code.call(event.getValue()); - } - }; - } - - /** - * @deprecated since 0.11.0; explicit groovy utilities/support will be deleted. - */ + /** @deprecated since 0.13.0, use {@link EntityAdjuncts#inferAdjunctStatus(org.apache.brooklyn.api.objs.EntityAdjunct)} */ @Deprecated - public static Policy newSingleSensorValuePolicy(final Sensor sensor, final Closure code) { - LOG.warn("Use of groovy.lang.Closure is deprecated in Policies.newSingleSensorValuePolicy()"); - return new AbstractPolicy() { - @Override - public void setEntity(EntityLocal entity) { - super.setEntity(entity); - entity.subscriptions().subscribe(entity, sensor, listenerFromValueClosure(code)); - } - }; - } - - /** - * @deprecated since 0.11.0; explicit groovy utilities/support will be deleted. - */ - @Deprecated - public static Policy newSingleSensorValuePolicy(final Entity remoteEntity, final Sensor remoteSensor, - final Closure code) { - LOG.warn("Use of groovy.lang.Closure is deprecated in Policies.newSingleSensorValuePolicy()"); - return new AbstractPolicy() { - @Override - public void setEntity(EntityLocal entity) { - super.setEntity(entity); - entity.subscriptions().subscribe(remoteEntity, remoteSensor, listenerFromValueClosure(code)); - } - }; - } - 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 EntityAdjuncts.inferAdjunctStatus(p); } } diff --git a/core/src/test/java/org/apache/brooklyn/core/entity/EntityConcurrencyTest.java b/core/src/test/java/org/apache/brooklyn/core/entity/EntityConcurrencyTest.java index f6062262aa..a4a77c082d 100644 --- a/core/src/test/java/org/apache/brooklyn/core/entity/EntityConcurrencyTest.java +++ b/core/src/test/java/org/apache/brooklyn/core/entity/EntityConcurrencyTest.java @@ -261,7 +261,7 @@ public void testConcurrentAddFeed() throws Exception { for (int i = 0; i < NUM_TASKS; i++) { ListenableFuture future = executor.submit(new Runnable() { @Override public void run() { - entity.feeds().addFeed(new MyFeed()); + entity.feeds().add(new MyFeed()); }}); futures.add(future); } diff --git a/launcher/src/test/java/org/apache/brooklyn/launcher/CleanOrphanedAdjunctsTest.java b/launcher/src/test/java/org/apache/brooklyn/launcher/CleanOrphanedAdjunctsTest.java index f9e772adfd..b8f8fef13c 100644 --- a/launcher/src/test/java/org/apache/brooklyn/launcher/CleanOrphanedAdjunctsTest.java +++ b/launcher/src/test/java/org/apache/brooklyn/launcher/CleanOrphanedAdjunctsTest.java @@ -54,7 +54,8 @@ public void testDeletesOrphanedPolicies() throws Exception { @Test public void testDeletesOrphanedFeeds() throws Exception { EntityInternal entity = origApp.addChild(EntitySpec.create(TestEntity.class).impl(MyEntity.class)); - Feed feed = entity.feeds().add(new MyFeed()); + Feed feed = new MyFeed(); + entity.feeds().add(feed); MementoTweaker tweaker = new MementoTweaker(new Deletions().entities(entity.getId())); assertTransformDeletes(new Deletions().feeds(feed.getId()), tweaker); } @@ -63,7 +64,8 @@ public void testDeletesOrphanedFeeds() throws Exception { public void testKeepsReachableAdjuncts() throws Exception { MyPolicy policy = origApp.policies().add(PolicySpec.create(MyPolicy.class)); MyEnricher enricher = origApp.enrichers().add(EnricherSpec.create(MyEnricher.class)); - Feed feed = origApp.feeds().add(new MyFeed()); + Feed feed = new MyFeed(); + origApp.feeds().add(feed); // Double-check we have the state we expected for the assertions that it is unmodified! BrooklynMementoRawData origData = getRawData(); 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 0000000000..ee43441565 --- /dev/null +++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/AdjunctApi.java @@ -0,0 +1,237 @@ +/* + * 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.AdjunctDetail; +import org.apache.brooklyn.rest.domain.AdjunctSummary; +import org.apache.brooklyn.rest.domain.ConfigSummary; +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"), + @ApiResponse(code = 400, message = "Type is not known adjunct kind") + }) + public List 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 (policy, enricher, feed)", required = false) + @QueryParam("adjunctType") final String adjunctType); + + // TODO support YAML ? + @POST + @ApiOperation(value = "Create and add an adjunct (e.g. a policy, enricher, or feed) to this entity", 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 AdjunctDetail 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 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"), + @ApiResponse(code = 400, message = "Adjunct does not support start/stop") + }) + 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"), + @ApiResponse(code = 400, message = "Adjunct does not support start/stop") + }) + 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.ConfigSummary.class, + responseContainer = "List") + @ApiResponses(value = { + @ApiResponse(code = 404, message = "Could not find application or entity or adjunct") + }) + public List 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 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); +} diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/CatalogApi.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/CatalogApi.java index 6499f14818..2c115df5b2 100644 --- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/CatalogApi.java +++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/CatalogApi.java @@ -58,7 +58,7 @@ public interface CatalogApi { @Consumes("application/json-deprecated") // prevent this from taking things @POST @ApiOperation( - value = "Add a catalog items (e.g. new type of entity, policy or location) by uploading YAML descriptor.", + value = "Add a catalog items (e.g. new type of entity, policy or location) by uploading YAML descriptor (deprecated, use POST of yaml or ZIP/JAR instead)", notes = "Return value is map of ID to CatalogItemSummary.", response = String.class, hidden = true @@ -159,7 +159,7 @@ public Response createFromUpload( @DELETE @Path("/applications/{symbolicName}/{version}") @ApiOperation( - value = "Deletes a specific version of an application's definition from the catalog", + value = "Deletes a specific version of an application's definition from the catalog (deprecated, use /catalog/bundles endpoint instead)", notes = "Version must exists, otherwise the API will return a 404. Alternatively, passing 'latest' will" + "pick up the latest version for the given 'symbolicName'" ) @@ -179,7 +179,7 @@ public void deleteApplication( @DELETE @Path("/entities/{symbolicName}/{version}") @ApiOperation( - value = "Deletes a specific version of an entity's definition from the catalog", + value = "Deletes a specific version of an entity's definition from the catalog (deprecated, use /catalog/bundles endpoint instead, as we add/delete bundles now)", notes = "Version must exists, otherwise the API will return a 404. Alternatively, passing 'latest' will" + "pick up the latest version for the given 'symbolicName'" ) @@ -199,7 +199,7 @@ public void deleteEntity( @DELETE @Path("/policies/{policyId}/{version}") @ApiOperation( - value = "Deletes a specific version of an policy's definition from the catalog", + value = "Deletes a specific version of an policy's definition from the catalog (deprecated, use /catalog/bundles endpoint instead, as we add/delete bundles now)", notes = "Version must exists, otherwise the API will return a 404. Alternatively, passing 'latest' will" + "pick up the latest version for the given 'policyId'") @ApiResponses(value = { @@ -218,7 +218,7 @@ public void deletePolicy( @DELETE @Path("/locations/{locationId}/{version}") @ApiOperation( - value = "Deletes a specific version of an location's definition from the catalog", + value = "Deletes a specific version of an location's definition from the catalog (deprecated, use /catalog/bundles endpoint instead, as we add/delete bundles now)", notes = "Version must exists, otherwise the API will return a 404. Alternatively, passing 'latest' will" + "pick up the latest version for the given 'locationId'" ) @@ -237,7 +237,7 @@ public void deleteLocation( @Deprecated @GET @Path("/entities") - @ApiOperation(value = "List available entity types optionally matching a query", + @ApiOperation(value = "List available entity types optionally matching a query (deprecated, use /catalog/types endpoint instead, with supertype=entity)", response = CatalogItemSummary.class, responseContainer = "List") public List listEntities( @@ -254,7 +254,7 @@ public List listEntities( @Deprecated @GET @Path("/applications") - @ApiOperation(value = "Fetch a list of templates (for applications) optionally matching a query", + @ApiOperation(value = "Fetch a list of templates (for applications) optionally matching a query (deprecated, use /catalog/types endpoint instead, with supertype=application; note some semantics of templates are changing as definition becomes more precise)", response = CatalogItemSummary.class, responseContainer = "List") public List listApplications( @@ -271,7 +271,7 @@ public List listApplications( @GET @Path("/entities/{symbolicName}/{version}") @ApiOperation( - value = "Fetch a specific version of an entity's definition from the catalog", + value = "Fetch a specific version of an entity's definition from the catalog (deprecated, use /catalog/types endpoint instead)", notes = "Version must exists, otherwise the API will return a 404. Alternatively, passing 'latest' will" + "pick up the latest version for the given 'symbolicName'", response = CatalogEntitySummary.class, @@ -292,7 +292,7 @@ public CatalogEntitySummary getEntity( @GET @Path("/applications/{symbolicName}/{version}") @ApiOperation( - value = "Fetch a specific version of an application's definition from the catalog", + value = "Fetch a specific version of an application's definition from the catalog (deprecated, use /catalog/types endpoint instead)", notes = "Version must exists, otherwise the API will return a 404. Alternatively, passing 'latest' will" + "pick up the latest version for the given 'symbolicName'", response = CatalogEntitySummary.class, @@ -312,7 +312,7 @@ public CatalogEntitySummary getApplication( @Deprecated @GET @Path("/policies") - @ApiOperation(value = "List available policies optionally matching a query", + @ApiOperation(value = "List available policies optionally matching a query (deprecated, use /catalog/types endpoint instead)", response = CatalogPolicySummary.class, responseContainer = "List") public List listPolicies( @@ -329,7 +329,7 @@ public List listPolicies( @GET @Path("/policies/{policyId}/{version}") @ApiOperation( - value = "Fetch a policy's definition from the catalog", + value = "Fetch a policy's definition from the catalog (deprecated, use /catalog/types endpoint instead)", notes = "Version must exists, otherwise the API will return a 404. Alternatively, passing 'latest' will" + "pick up the latest version for the given 'policyId'", response = CatalogItemSummary.class, @@ -348,7 +348,7 @@ public CatalogPolicySummary getPolicy( @Deprecated @GET @Path("/locations") - @ApiOperation(value = "List available locations optionally matching a query", + @ApiOperation(value = "List available locations optionally matching a query (deprecated, use /catalog/types endpoint instead)", response = CatalogLocationSummary.class, responseContainer = "List") public List listLocations( @@ -365,7 +365,7 @@ public List listLocations( @GET @Path("/locations/{locationId}/{version}") @ApiOperation( - value = "Fetch a location's definition from the catalog", + value = "Fetch a location's definition from the catalog (deprecated, use /catalog/types endpoint instead)", notes = "Version must exists, otherwise the API will return a 404. Alternatively, passing 'latest' will" + "pick up the latest version for the given 'locationId'", response = CatalogItemSummary.class, @@ -406,6 +406,7 @@ public Response getIcon( @ApiResponses(value = { @ApiResponse(code = 404, message = "Undefined catalog item"), }) + @ApiOperation(value = "Deprecate an item (deprecated, use /catalog/types endpoint instead, but disabled/deprecating is not supported for individual types)") @Path("/entities/{itemId}/deprecated") public void setDeprecated( @ApiParam(name = "itemId", value = "The ID of the catalog item to be deprecated", required = true) @@ -419,6 +420,7 @@ public void setDeprecated( @Deprecated @POST @Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_OCTET_STREAM, MediaType.TEXT_PLAIN}) + @ApiOperation(value = "Disable an item (deprecated, use /catalog/types endpoint instead, but disabled/deprecating is not supported for individual types)") @ApiResponses(value = { @ApiResponse(code = 404, message = "Undefined catalog item"), }) @@ -434,7 +436,7 @@ public void setDisabled( @Deprecated @GET @Path("/enrichers") - @ApiOperation(value = "List available enrichers types optionally matching a query", + @ApiOperation(value = "List available enrichers types optionally matching a query (deprecated, use /catalog/types endpoint instead)", response = CatalogItemSummary.class, responseContainer = "List") public List listEnrichers( @@ -450,7 +452,7 @@ public List listEnrichers( @Deprecated @GET @Path("/enrichers/{enricherId}/{version}") - @ApiOperation(value = "Fetch an enricher's definition from the catalog", + @ApiOperation(value = "Fetch an enricher's definition from the catalog (deprecated, use /catalog/types endpoint instead)", response = CatalogItemSummary.class, responseContainer = "List") @ApiResponses(value = { @@ -467,7 +469,7 @@ public CatalogEnricherSummary getEnricher( @Deprecated @DELETE @Path("/enrichers/{enricherId}/{version}") - @ApiOperation(value = "Deletes a specific version of an enricher's definition from the catalog") + @ApiOperation(value = "Deletes a specific version of an enricher's definition from the catalog (deprecated, use /catalog/types endpoint instead)") @ApiResponses(value = { @ApiResponse(code = 404, message = "Enricher not found") }) diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/EntityConfigApi.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/EntityConfigApi.java index 4a5bbde000..a0e36049e4 100644 --- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/EntityConfigApi.java +++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/EntityConfigApi.java @@ -18,7 +18,6 @@ */ package org.apache.brooklyn.rest.api; -import io.swagger.annotations.Api; import java.util.List; import java.util.Map; @@ -32,12 +31,13 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; -import org.apache.brooklyn.rest.domain.EntityConfigSummary; +import org.apache.brooklyn.rest.domain.ConfigSummary; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; +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}/config") @Api("Entity Config") @@ -52,7 +52,7 @@ public interface EntityConfigApi { @ApiResponses(value = { @ApiResponse(code = 404, message = "Could not find application or entity") }) - public List list( + public List list( @ApiParam(value = "Application ID or name", required = true) @PathParam("application") final String application, @ApiParam(value = "Entity ID or name", required = true) 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 a698f7de65..1bee2f84a5 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,10 +36,12 @@ @Api("Entity Policies") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) +/** @deprecated since 0.13.0 use AdjunctApi */ +@Deprecated public interface PolicyApi { @GET - @ApiOperation(value = "Fetch the policies attached to a specific application entity", + @ApiOperation(value = "Fetch the policies attached to a specific application entity (deprecated, use adjuncts/ endpoint instead)", response = org.apache.brooklyn.rest.domain.PolicySummary.class, responseContainer = "List") @ApiResponses(value = { @@ -55,7 +57,7 @@ public List list( // (and in sensors class) @GET @Path("/current-state") - @ApiOperation(value = "Fetch policy states in batch", notes="Returns a map of policy ID to whether it is active") + @ApiOperation(value = "Fetch policy states in batch", notes="Returns a map of policy ID to whether it is active (deprecated, use adjuncts/ endpoint instead)") // FIXME method name -- this is nothing to do with config! public Map batchConfigRead( @ApiParam(value = "Application ID or name", required = true) @@ -64,7 +66,7 @@ public Map batchConfigRead( @PathParam("entity") String entityToken) ; @POST - @ApiOperation(value = "Add a policy", notes = "Returns a summary of the new policy") + @ApiOperation(value = "Add a policy", notes = "Returns a summary of the new policy (deprecated, use adjuncts/ endpoint instead)") @ApiResponses(value = { @ApiResponse(code = 404, message = "Could not find application or entity"), @ApiResponse(code = 400, message = "Type is not a class implementing Policy") @@ -86,7 +88,7 @@ public PolicySummary addPolicy( @GET @Path("/{policy}") - @ApiOperation(value = "Gets status of a policy (RUNNING / SUSPENDED)") + @ApiOperation(value = "Gets status of a policy (RUNNING / SUSPENDED) (deprecated, use adjuncts/ endpoint instead)") @ApiResponses(value = { @ApiResponse(code = 404, message = "Could not find application, entity or policy") }) @@ -102,7 +104,7 @@ public Status getStatus( @POST @Path("/{policy}/start") - @ApiOperation(value = "Start or resume a policy") + @ApiOperation(value = "Start or resume a policy (deprecated, use adjuncts/ endpoint instead)") @ApiResponses(value = { @ApiResponse(code = 404, message = "Could not find application, entity or policy") }) @@ -118,7 +120,7 @@ public Response start( @POST @Path("/{policy}/stop") - @ApiOperation(value = "Suspends a policy") + @ApiOperation(value = "Suspends a policy (deprecated, use adjuncts/ endpoint instead)") @ApiResponses(value = { @ApiResponse(code = 404, message = "Could not find application, entity or policy") }) @@ -135,7 +137,7 @@ public Response stop( // TODO: Should be DELETE /policy, not POST /policy/destroy @POST @Path("/{policy}/destroy") - @ApiOperation(value = "Destroy a policy", notes="Removes a policy from being associated with the entity and destroys it (stopping first if running)") + @ApiOperation(value = "Destroy a policy (deprecated, use adjuncts/ endpoint instead)", notes="Removes a policy 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 policy") }) 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 1e6bfe67a7..f38d9d41fe 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,27 +18,36 @@ */ 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.13.0 use AdjunctApi */ +@Deprecated public interface PolicyConfigApi { @GET - @ApiOperation(value = "Fetch the config keys for a specific policy", + @ApiOperation(value = "Fetch the config keys for a specific policy (deprecated, use adjuncts/ endpoint instead)", response = org.apache.brooklyn.rest.domain.ConfigSummary.class, responseContainer = "List") @ApiResponses(value = { @@ -56,7 +65,7 @@ public List list( // (and in sensors class) @GET @Path("/current-state") - @ApiOperation(value = "Fetch config key values in batch", notes="Returns a map of config name to value") + @ApiOperation(value = "Fetch config key values in batch (deprecated, use adjuncts/ endpoint instead)", notes="Returns a map of config name to value") public Map batchConfigRead( @ApiParam(value = "Application ID or name", required = true) @PathParam("application") String application, @@ -67,7 +76,7 @@ public Map batchConfigRead( @GET @Path("/{config}") - @ApiOperation(value = "Fetch config value", response = Object.class) + @ApiOperation(value = "Fetch config value (deprecated, use adjuncts/ endpoint instead)", response = Object.class) @ApiResponses(value = { @ApiResponse(code = 404, message = "Could not find application, entity, policy or config key") }) @@ -84,7 +93,7 @@ public String get( @POST @Path("/{config}") @Consumes(value = {"*/*"}) - @ApiOperation(value = "Sets the given config on this policy") + @ApiOperation(value = "Sets the given config on this policy (deprecated, use adjuncts/ endpoint instead)") @ApiResponses(value = { @ApiResponse(code = 404, message = "Could not find application, entity, policy or config key") }) 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 0000000000..a2f348c54f --- /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 tags; + @JsonInclude(Include.NON_EMPTY) + final Set parameters = MutableSet.of(); + final Map 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 getTags() { + return tags; + } + + public AdjunctDetail parameter(ConfigSummary p) { + parameters.add(p); return this; + } + + public AdjunctDetail config(String key, Object val) { + config.put(key, val); return this; + } + + public AdjunctDetail config(Map vals) { + config.putAll(vals); return this; + } + +} 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 0000000000..97ce1b23f2 --- /dev/null +++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/AdjunctSummary.java @@ -0,0 +1,148 @@ +/* + * 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 highlights; + + private Map 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(); + } + + protected AdjunctSummary( + String id, + String name, + BrooklynObjectType adjunctType, + String catalogItemId, + Status state, + Map highlights, + Map 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. 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 getHighlights() { + return highlights; + } + + public Map getLinks() { + return links; + } + + public AdjunctSummary state(Status state) { + this.state = state; return this; + } + + public AdjunctSummary links(Map 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 + + '}'; + } + +} diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/ApplicationSpec.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/ApplicationSpec.java index 52f7efd82e..172f66f6d7 100644 --- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/ApplicationSpec.java +++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/ApplicationSpec.java @@ -27,8 +27,9 @@ import java.util.Objects; import java.util.Set; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -85,12 +86,13 @@ public ApplicationSpec build() { } private final String name; - @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(Include.NON_NULL) private final String type; - @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(Include.NON_NULL) private final Set entities; + @JsonInclude(Include.NON_NULL) private final Set locations; - @JsonSerialize(include = JsonSerialize.Inclusion.NON_EMPTY) + @JsonInclude(Include.NON_EMPTY) private final Map config; public ApplicationSpec( @@ -106,7 +108,7 @@ public ApplicationSpec( } else { this.entities = (entities.isEmpty() && type!=null) ? null : ImmutableSet.copyOf(entities); } - this.locations = ImmutableSet.copyOf(checkNotNull(locations, "locations must be provided for an application spec")); + this.locations = locations == null ? Collections.emptySet() : ImmutableSet.copyOf(locations); this.config = config == null ? Collections.emptyMap() : ImmutableMap.copyOf(config); if (this.entities!=null && this.type!=null) throw new IllegalStateException("cannot supply both type and entities for an application spec"); // valid for both to be null, e.g. for an anonymous type 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 2e81ae1709..56ff0ff334 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 @@ -23,10 +23,14 @@ import java.util.Objects; import java.util.Set; +import org.apache.brooklyn.rest.api.TypeApi; + import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.google.common.collect.ImmutableSet; +/** @deprecated since 0.13.0 new {@link TypeApi} returns {@link TypeSummary} */ +@Deprecated public class CatalogEnricherSummary extends CatalogItemSummary { private static final long serialVersionUID = -588856488327394445L; 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 5947b7f06c..6df6b383a1 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 @@ -23,10 +23,14 @@ import java.util.Objects; import java.util.Set; +import org.apache.brooklyn.rest.api.TypeApi; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonSerialize; +/** @deprecated since 0.13.0 new {@link TypeApi} returns {@link TypeSummary} */ +@Deprecated public class CatalogEntitySummary extends CatalogItemSummary { private static final long serialVersionUID = 1063908984191424539L; 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 cdb86d4eec..19a1749011 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 @@ -26,6 +26,8 @@ import java.util.Objects; import java.util.Set; +import org.apache.brooklyn.rest.api.TypeApi; + import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonSerialize; @@ -36,6 +38,8 @@ * see also, subclasses */ @JsonIgnoreProperties(ignoreUnknown = true) // ignore unknown, ie properties from subclasses (entity) +/** @deprecated since 0.13.0 new {@link TypeApi} returns {@link TypeSummary} */ +@Deprecated public class CatalogItemSummary implements HasId, HasName, Serializable { private static final long serialVersionUID = -823483595879417681L; 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 0213a0c890..1b769489af 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 @@ -23,9 +23,13 @@ import java.util.Objects; import java.util.Set; +import org.apache.brooklyn.rest.api.TypeApi; + import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.ImmutableSet; +/** @deprecated since 0.13.0 new {@link TypeApi} returns {@link TypeSummary} */ +@Deprecated public class CatalogLocationSummary extends CatalogItemSummary { private static final long serialVersionUID = 8420991584336514673L; 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 37e5786d2b..dafa6bb1a9 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 @@ -23,10 +23,14 @@ import java.util.Objects; import java.util.Set; +import org.apache.brooklyn.rest.api.TypeApi; + import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.google.common.collect.ImmutableSet; +/** @deprecated since 0.13.0 new {@link TypeApi} returns {@link TypeSummary} */ +@Deprecated public class CatalogPolicySummary extends CatalogItemSummary { private static final long serialVersionUID = -588856488327394445L; diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EffectorSummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EffectorSummary.java index 3e09c73f3f..7f38438cd1 100644 --- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EffectorSummary.java +++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EffectorSummary.java @@ -23,8 +23,9 @@ import java.util.Map; import java.util.Set; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.google.common.base.MoreObjects; import com.google.common.base.Objects; import com.google.common.collect.ImmutableMap; @@ -38,7 +39,7 @@ public static class ParameterSummary implements HasName, Serializable { private final String name; private final String type; - @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(Include.NON_NULL) private final String description; private final T defaultValue; private final boolean shouldSanitize; @@ -110,9 +111,9 @@ public String toString() { private final String name; private final String returnType; private final Set> parameters; - @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(Include.NON_EMPTY) private final String description; - @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(Include.NON_EMPTY) private final Map links; public EffectorSummary( diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EntityDetail.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EntityDetail.java index 567c40bda4..f49bb81362 100644 --- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EntityDetail.java +++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EntityDetail.java @@ -18,23 +18,25 @@ */ package org.apache.brooklyn.rest.domain; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.google.common.collect.ImmutableList; -import org.apache.brooklyn.core.entity.lifecycle.Lifecycle; - import java.net.URI; import java.util.Collections; import java.util.List; import java.util.Map; +import org.apache.brooklyn.core.entity.lifecycle.Lifecycle; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.collect.ImmutableList; + public class EntityDetail extends EntitySummary { private static final long serialVersionUID = 100490507982229165L; private final String applicationId; private final String parentId; - @JsonSerialize(include = JsonSerialize.Inclusion.NON_EMPTY) + @JsonInclude(Include.NON_EMPTY) private final List children; private final List groupIds; private final List> members; diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EntitySummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EntitySummary.java index e0a585a116..ae123ef421 100644 --- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EntitySummary.java +++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EntitySummary.java @@ -18,15 +18,16 @@ */ package org.apache.brooklyn.rest.domain; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.google.common.collect.ImmutableMap; - import java.io.Serializable; import java.net.URI; import java.util.Map; import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.collect.ImmutableMap; + public class EntitySummary implements HasId, HasName, Serializable { private static final long serialVersionUID = 100490507982229165L; @@ -34,7 +35,7 @@ public class EntitySummary implements HasId, HasName, Serializable { private final String id; private final String name; private final String type; - @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(Include.NON_NULL) private final String catalogItemId; private final Map links; 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 338604514b..4098ee17c1 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 @@ -26,7 +26,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; -/** @deprecated since 0.13.0 no different to ConfigSummary, use that */ +/** @deprecated since 0.13.0 no different to {@link ConfigSummary}, use that */ @Deprecated public class PolicyConfigSummary extends ConfigSummary { 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 ce9c9e0ca1..1d0e9307d2 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.13.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 links; - private final Map highlights; - public PolicySummary( @JsonProperty("id") String id, @JsonProperty("name") String name, @@ -48,67 +39,7 @@ public PolicySummary( @JsonProperty("state") Status state, @JsonProperty("highlights") Map highlights, @JsonProperty("links") Map links) { - this.id = id; - this.name = name; - this.catalogItemId = catalogItemId; - this.state = state; - this.links = (links == null) ? ImmutableMap. 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 getLinks() { - return links; - } - - public Map 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 + - '}'; - } } diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/ScriptExecutionSummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/ScriptExecutionSummary.java index c84573c3d9..326601895e 100644 --- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/ScriptExecutionSummary.java +++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/ScriptExecutionSummary.java @@ -21,20 +21,21 @@ import java.io.Serializable; import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; public class ScriptExecutionSummary implements Serializable { private static final long serialVersionUID = -7707936602991185960L; - @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(Include.NON_NULL) private final Object result; - @JsonSerialize(include = JsonSerialize.Inclusion.NON_EMPTY) + @JsonInclude(Include.NON_EMPTY) private final String problem; - @JsonSerialize(include = JsonSerialize.Inclusion.NON_EMPTY) + @JsonInclude(Include.NON_EMPTY) private final String stdout; - @JsonSerialize(include = JsonSerialize.Inclusion.NON_EMPTY) + @JsonInclude(Include.NON_EMPTY) private final String stderr; public ScriptExecutionSummary( diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/SensorSummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/SensorSummary.java index 11a4d2f519..9442f33e16 100644 --- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/SensorSummary.java +++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/SensorSummary.java @@ -23,8 +23,9 @@ import java.util.Map; import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.google.common.collect.ImmutableMap; public class SensorSummary implements HasName, Serializable { @@ -33,9 +34,9 @@ public class SensorSummary implements HasName, Serializable { private final String name; private final String type; - @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(Include.NON_NULL) private final String description; - @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(Include.NON_NULL) private final Map links; public SensorSummary( 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 e2b2ce4d3e..4a998c7ab4 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, 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 e172127d33..86ff290ce5 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 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; @@ -61,6 +62,7 @@ public static Iterable getBrooklynRestResources() 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()); 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 0000000000..111119303e --- /dev/null +++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/AdjunctResource.java @@ -0,0 +1,273 @@ +/* + * 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.AdjunctDetail; +import org.apache.brooklyn.rest.domain.AdjunctSummary; +import org.apache.brooklyn.rest.domain.ConfigSummary; +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.ConfigTransformer; +import org.apache.brooklyn.rest.transform.EntityTransformer; +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 list(String application, String entityToken, String adjunctType) { + final Entity entity = brooklyn().getEntity(application, entityToken); + Iterable 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() { + @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 AdjunctDetail addAdjunct(String application, String entityToken, String adjunctTypeName, Map 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 { + // shouldn't come here + throw WebResourceUtils.badRequest("Unexpected adjunct type %s", adjunct); + } + + return Response.status(Response.Status.NO_CONTENT).build(); + } + + // ---- config ---- + + @Override + public List listConfig( + final String application, final String entityToken, final String adjunctToken) { + Entity entity = brooklyn().getEntity(application, entityToken); + EntityAdjunct adjunct = brooklyn().getAdjunct(entity, adjunctToken); + + List result = Lists.newArrayList(); + for (ConfigKey key : adjunct.config().findKeysDeclared(Predicates.alwaysTrue())) { + result.add(ConfigTransformer.of(key).on(entity, adjunct).includeLinks(ui.getBaseUriBuilder(), false, true).transform()); + } + return result; + } + + // TODO support parameters ?show=value,summary&name=xxx &format={string,json,xml} + // (and in sensors class) + @Override + public Map batchConfigRead(String application, String entityToken, String adjunctToken) { + 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> cki = adjunct.config().findKeysDeclared(ConfigPredicates.nameSatisfies(Predicates.equalTo(configKeyName))); + // TODO try deprecated names? + if (cki.isEmpty()) throw WebResourceUtils.notFound("Cannot find config key '%s' in adjunct '%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> cki = adjunct.config().findKeysDeclared(ConfigPredicates.nameSatisfies(Predicates.equalTo(configKeyName))); + // TODO try deprecated names? + if (cki.isEmpty()) throw WebResourceUtils.notFound("Cannot find config key '%s' in adjunct '%s' of entity '%s'", configKeyName, adjunctToken, entityToken); + ConfigKey ck = cki.iterator().next(); + + adjunct.config().set((ConfigKey) ck, TypeCoercions.coerce(value, ck.getTypeToken())); + + return Response.status(Response.Status.OK).build(); + } + +} diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java index 65e695675c..f95c1ee3b9 100644 --- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java +++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java @@ -84,6 +84,7 @@ public class CatalogResource extends AbstractBrooklynRestResource implements Cat private static final Logger log = LoggerFactory.getLogger(CatalogResource.class); private static final String LATEST = "latest"; + @Deprecated private Function toCatalogItemSummary(final UriInfo ui) { return new Function() { @Override @@ -202,11 +203,13 @@ private Response buildCreateResponse(Iterable catalogItems) { } @Override + @Deprecated public void deleteApplication(String symbolicName, String version) throws Exception { deleteEntity(symbolicName, version); } @Override + @Deprecated 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.forbidden("User '%s' is not authorized to modify catalog", @@ -226,6 +229,7 @@ public void deleteEntity(String symbolicName, String version) throws Exception { } @Override + @Deprecated 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.forbidden("User '%s' is not authorized to modify catalog", @@ -245,6 +249,7 @@ public void deletePolicy(String policyId, String version) throws Exception { } @Override + @Deprecated 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.forbidden("User '%s' is not authorized to modify catalog", @@ -264,6 +269,7 @@ public void deleteLocation(String locationId, String version) throws Exception { } @Override + @Deprecated public List listEntities(String regex, String fragment, boolean allVersions) { Predicate filter = Predicates.and( @@ -274,6 +280,7 @@ public List listEntities(String regex, String fragment, bo } @Override + @Deprecated public List listApplications(String regex, String fragment, boolean allVersions) { @SuppressWarnings("unchecked") Predicate filter = @@ -285,6 +292,7 @@ public List listApplications(String regex, String fragment, } @Override + @Deprecated public CatalogEntitySummary getEntity(String symbolicName, String version) { if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_CATALOG_ITEM, symbolicName+(Strings.isBlank(version)?"":":"+version))) { throw WebResourceUtils.forbidden("User '%s' is not authorized to see catalog entry", @@ -302,11 +310,13 @@ public CatalogEntitySummary getEntity(String symbolicName, String version) { } @Override + @Deprecated public CatalogEntitySummary getApplication(String symbolicName, String version) { return getEntity(symbolicName, version); } @Override + @Deprecated public List listPolicies(String regex, String fragment, boolean allVersions) { Predicate filter = Predicates.and( @@ -317,6 +327,7 @@ public List listPolicies(String regex, String fragment, bo } @Override + @Deprecated 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.forbidden("User '%s' is not authorized to see catalog entry", @@ -333,6 +344,7 @@ public CatalogPolicySummary getPolicy(String policyId, String version) throws Ex } @Override + @Deprecated public List listLocations(String regex, String fragment, boolean allVersions) { Predicate filter = Predicates.and( @@ -343,6 +355,7 @@ public List listLocations(String regex, String fragment, } @Override + @Deprecated 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.forbidden("User '%s' is not authorized to see catalog entry", @@ -359,6 +372,7 @@ public CatalogLocationSummary getLocation(String locationId, String version) thr } @SuppressWarnings({ "unchecked", "rawtypes" }) + @Deprecated private List getCatalogItemSummariesMatchingRegexFragment( Predicate type, String regex, String fragment, boolean allVersions) { List> filters = new ArrayList(); @@ -390,8 +404,8 @@ public Response getIcon(String itemId, String version) { return getCatalogItemIcon(mgmt().getTypeRegistry().get(itemId, version)); } - @SuppressWarnings("deprecation") @Override + @Deprecated public void setDeprecated(String itemId, boolean deprecated) { if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.MODIFY_CATALOG_ITEM, StringAndArgument.of(itemId, "deprecated"))) { throw WebResourceUtils.forbidden("User '%s' is not authorized to modify catalog", @@ -400,8 +414,8 @@ public void setDeprecated(String itemId, boolean deprecated) { CatalogUtils.setDeprecated(mgmt(), itemId, deprecated); } - @SuppressWarnings("deprecation") @Override + @Deprecated public void setDisabled(String itemId, boolean disabled) { if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.MODIFY_CATALOG_ITEM, StringAndArgument.of(itemId, "disabled"))) { throw WebResourceUtils.forbidden("User '%s' is not authorized to modify catalog", @@ -411,6 +425,7 @@ public void setDisabled(String itemId, boolean disabled) { } @Override + @Deprecated public List listEnrichers(@ApiParam(name = "regex", value = "Regular expression to search for") @DefaultValue("") String regex, @ApiParam(name = "fragment", value = "Substring case-insensitive to search for") @DefaultValue("") String fragment, @ApiParam(name = "allVersions", value = "Include all versions (defaults false, only returning the best version)") @DefaultValue("false") boolean includeAllVersions) { Predicate filter = Predicates.and( @@ -421,6 +436,7 @@ public List listEnrichers(@ApiParam(name = "regex", valu } @Override + @Deprecated public CatalogEnricherSummary getEnricher(@ApiParam(name = "enricherId", value = "The ID of the enricher to retrieve", required = true) String enricherId, @ApiParam(name = "version", value = "The version identifier of the enricher to retrieve", required = true) String version) throws Exception { if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_CATALOG_ITEM, enricherId+(Strings.isBlank(version)?"":":"+version))) { throw WebResourceUtils.forbidden("User '%s' is not authorized to see catalog entry", @@ -436,6 +452,7 @@ public CatalogEnricherSummary getEnricher(@ApiParam(name = "enricherId", value = } @Override + @Deprecated public void deleteEnricher(@ApiParam(name = "enricherId", value = "The ID of the enricher to delete", required = true) String enricherId, @ApiParam(name = "version", value = "The version identifier of the enricher to delete", required = true) String version) throws Exception { if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.MODIFY_CATALOG_ITEM, StringAndArgument.of(enricherId+(Strings.isBlank(version) ? "" : ":"+version), "delete"))) { throw WebResourceUtils.forbidden("User '%s' is not authorized to modify catalog", diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/EntityConfigResource.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/EntityConfigResource.java index d85f1d76d0..3431b99f5d 100644 --- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/EntityConfigResource.java +++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/EntityConfigResource.java @@ -32,9 +32,9 @@ import org.apache.brooklyn.core.mgmt.entitlement.Entitlements; import org.apache.brooklyn.core.mgmt.entitlement.Entitlements.EntityAndItem; import org.apache.brooklyn.rest.api.EntityConfigApi; -import org.apache.brooklyn.rest.domain.EntityConfigSummary; +import org.apache.brooklyn.rest.domain.ConfigSummary; import org.apache.brooklyn.rest.filter.HaHotStateRequired; -import org.apache.brooklyn.rest.transform.EntityTransformer; +import org.apache.brooklyn.rest.transform.ConfigTransformer; import org.apache.brooklyn.rest.util.WebResourceUtils; import org.apache.brooklyn.util.core.flags.TypeCoercions; import org.apache.brooklyn.util.core.task.Tasks; @@ -52,7 +52,7 @@ public class EntityConfigResource extends AbstractBrooklynRestResource implement private static final Logger LOG = LoggerFactory.getLogger(EntityConfigResource.class); @Override - public List list(final String application, final String entityToken) { + public List list(final String application, final String entityToken) { final Entity entity = brooklyn().getEntity(application, entityToken); if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_ENTITY, entity)) { throw WebResourceUtils.forbidden("User '%s' is not authorized to see entity '%s'", @@ -61,7 +61,7 @@ public List list(final String application, final String ent // TODO merge with keys which have values: // ((EntityInternal) entity).config().getBag().getAllConfigAsConfigKeyMap(); - List result = Lists.newArrayList(); + List result = Lists.newArrayList(); for (ConfigKey key : entity.getEntityType().getConfigKeys()) { // Exclude config that user is not allowed to see @@ -70,7 +70,7 @@ public List list(final String application, final String ent new Object[] {Entitlements.getEntitlementContext().user(), key.getName(), entity}); continue; } - result.add(EntityTransformer.entityConfigSummary(entity, key, ui.getBaseUriBuilder())); + result.add(ConfigTransformer.of(key).on(entity).includeLinks(ui.getBaseUriBuilder(), true, true).transform()); } return result; 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 6dcdf197fa..1fca535196 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.Maps; @HaHotStateRequired +@Deprecated public class PolicyConfigResource extends AbstractBrooklynRestResource implements PolicyConfigApi { @Override 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 0b8d2c25c3..924d3fd0c0 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.Maps; @HaHotStateRequired +@Deprecated public class PolicyResource extends AbstractBrooklynRestResource implements PolicyApi { private static final Logger log = LoggerFactory.getLogger(PolicyResource.class); @@ -70,7 +71,6 @@ public PolicySummary apply(Policy policy) { // (and in sensors class) @Override public Map batchConfigRead( String application, String entityToken) { - // TODO: add test Entity entity = brooklyn().getEntity(application, entityToken); Map result = Maps.newLinkedHashMap(); for (Policy p : entity.policies()) { 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 0000000000..f6598ecd4c --- /dev/null +++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/AdjunctTransformer.java @@ -0,0 +1,96 @@ +/* + * 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.entity.EntityAdjuncts; +import org.apache.brooklyn.rest.api.AdjunctApi; +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.util.BrooklynRestResourceUtils; +import org.apache.brooklyn.util.collections.MutableMap; + +import com.google.common.base.Predicates; + +/** + * 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 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(ConfigTransformer.of(key).on(entity, adjunct).includeLinks(ub, false, true).transform()); + } + result.config(EntityTransformer.getConfigValues(utils, adjunct)); + return result; + } + + protected static Map buildLinks(Entity entity, EntityAdjunct adjunct, UriBuilder ub, boolean detail) { + MutableMap links = MutableMap.of(); + + links.put("self", serviceUriBuilder(ub, AdjunctApi.class, "get").build(entity.getApplicationId(), entity.getId(), adjunct.getId())); + + if (detail) { + links.put("application", EntityTransformer.applicationUri(entity.getApplication(), ub) ); + links.put("entity", EntityTransformer.entityUri(entity, ub) ); + links.put("adjunct", adjunctUri(entity, adjunct, ub) ); + + 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( EntityAdjuncts.inferAdjunctStatus(adjunct) ); + } + + public static URI adjunctUri(Entity entity, EntityAdjunct adjunct, UriBuilder ub) { + return serviceUriBuilder(ub, AdjunctApi.class, "get").build(entity.getApplicationId(), entity.getId(), adjunct.getId()); + } + +} diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/CatalogTransformer.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/CatalogTransformer.java index aa30dd5112..8bff9f14fd 100644 --- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/CatalogTransformer.java +++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/CatalogTransformer.java @@ -71,6 +71,8 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; +/** @deprecated since 0.13.0 use {@link RegisteredType} methods in {@link TypeTransformer} */ +@Deprecated public class CatalogTransformer { private static final org.slf4j.Logger log = LoggerFactory.getLogger(CatalogTransformer.class); @@ -87,9 +89,9 @@ public static CatalogEntitySummary catalogEntitySummary(Brook EntityDynamicType typeMap = BrooklynTypes.getDefinedEntityType(spec.getType()); EntityType type = typeMap.getSnapshot(); - AtomicInteger paramPriorityCnt = new AtomicInteger(); + AtomicInteger priority = new AtomicInteger(); for (SpecParameter input: spec.getParameters()) - config.add(EntityTransformer.entityConfigSummary(input, paramPriorityCnt)); + config.add(ConfigTransformer.of(input).uiIncrementAndSetPriorityIfPinned(priority).transformLegacyEntityConfig()); for (Sensor x: type.getSensors()) sensors.add(SensorTransformer.sensorSummaryForCatalog(x)); for (Effector x: type.getEffectors()) @@ -149,8 +151,9 @@ public static CatalogPolicySummary catalogPolicySummary(BrooklynRestResourceUtil PolicySpec spec = null; try{ spec = b.getTypeRegistry().createSpec(item, null, PolicySpec.class); - for (final SpecParameter input : spec.getParameters()){ - config.add(EntityTransformer.policyConfigSummary(input)); + AtomicInteger priority = new AtomicInteger(); + for (SpecParameter input: spec.getParameters()) { + config.add(ConfigTransformer.of(input).uiIncrementAndSetPriorityIfPinned(priority).transformLegacyPolicyConfig()); } }catch (Exception e) { Exceptions.propagateIfFatal(e); @@ -169,8 +172,9 @@ public static CatalogEnricherSummary catalogEnricherSummary(BrooklynRestResource EnricherSpec spec = null; try{ spec = b.getTypeRegistry().createSpec(item, null, EnricherSpec.class); - for (final SpecParameter input : spec.getParameters()){ - config.add(EntityTransformer.enricherConfigSummary(input)); + AtomicInteger priority = new AtomicInteger(); + for (SpecParameter input: spec.getParameters()) { + config.add(ConfigTransformer.of(input).uiIncrementAndSetPriorityIfPinned(priority).transformLegacyEnricherConfig()); } }catch (Exception e) { Exceptions.propagateIfFatal(e); @@ -257,9 +261,9 @@ public static CatalogEntitySummary catalogEntitySummary(Brook EntityDynamicType typeMap = BrooklynTypes.getDefinedEntityType(spec.getType()); EntityType type = typeMap.getSnapshot(); - AtomicInteger paramPriorityCnt = new AtomicInteger(); + AtomicInteger priority = new AtomicInteger(); for (SpecParameter input: spec.getParameters()) - config.add(EntityTransformer.entityConfigSummary(input, paramPriorityCnt)); + config.add(ConfigTransformer.of(input).uiIncrementAndSetPriorityIfPinned(priority).transformLegacyEntityConfig()); for (Sensor x: type.getSensors()) sensors.add(SensorTransformer.sensorSummaryForCatalog(x)); for (Effector x: type.getEffectors()) @@ -315,8 +319,9 @@ public static CatalogPolicySummary catalogPolicySummary(BrooklynRestResourceUtil final Set config = Sets.newLinkedHashSet(); try{ final PolicySpec spec = (PolicySpec) b.getCatalog().peekSpec(item); - for (final SpecParameter input : spec.getParameters()){ - config.add(EntityTransformer.policyConfigSummary(input)); + AtomicInteger priority = new AtomicInteger(); + for (SpecParameter input: spec.getParameters()) { + config.add(ConfigTransformer.of(input).uiIncrementAndSetPriorityIfPinned(priority).transformLegacyPolicyConfig()); } }catch (Exception e) { Exceptions.propagateIfFatal(e); @@ -333,8 +338,9 @@ public static CatalogEnricherSummary catalogEnricherSummary(BrooklynRestResource final Set config = Sets.newLinkedHashSet(); try{ final EnricherSpec spec = (EnricherSpec) b.getCatalog().peekSpec(item); - for (final SpecParameter input : spec.getParameters()){ - config.add(EntityTransformer.enricherConfigSummary(input)); + AtomicInteger priority = new AtomicInteger(); + for (SpecParameter input: spec.getParameters()) { + config.add(ConfigTransformer.of(input).uiIncrementAndSetPriorityIfPinned(priority).transformLegacyEnricherConfig()); } }catch (Exception e) { Exceptions.propagateIfFatal(e); diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/ConfigTransformer.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/ConfigTransformer.java new file mode 100644 index 0000000000..314ce3c3d8 --- /dev/null +++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/ConfigTransformer.java @@ -0,0 +1,175 @@ +/* + * 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.lang.reflect.Field; +import java.net.URI; +import java.util.concurrent.atomic.AtomicInteger; + +import javax.ws.rs.core.UriBuilder; + +import org.apache.brooklyn.api.catalog.CatalogConfig; +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.api.objs.BrooklynObject; +import org.apache.brooklyn.api.objs.EntityAdjunct; +import org.apache.brooklyn.api.objs.SpecParameter; +import org.apache.brooklyn.config.ConfigKey; +import org.apache.brooklyn.core.config.render.RendererHints; +import org.apache.brooklyn.rest.api.AdjunctApi; +import org.apache.brooklyn.rest.api.EntityConfigApi; +import org.apache.brooklyn.rest.domain.ConfigSummary; +import org.apache.brooklyn.util.collections.MutableMap; + +import com.google.common.collect.Iterables; + +public class ConfigTransformer { + + private final ConfigKey key; + + UriBuilder ub; + boolean includeContextLinks, includeActionLinks; + Entity entity; + EntityAdjunct adjunct; + + String label; + Double priority; + Boolean pinned; + + public static ConfigTransformer of(ConfigKey key) { + return new ConfigTransformer(key); + } + + public static ConfigTransformer of(SpecParameter param) { + ConfigTransformer result = of(param.getConfigKey()); + result.label = param.getLabel(); + result.pinned = param.isPinned(); + return result; + } + + private ConfigTransformer(ConfigKey key) { + this.key = key; + } + + public ConfigTransformer on(Entity entity) { + this.entity = entity; + return this; + } + + public ConfigTransformer on(Entity entity, EntityAdjunct adjunct) { + this.entity = entity; + this.adjunct = adjunct; + return this; + } + + /** configures this transformer to be able to include links, and which types of links to include; + * note {@link #on(Entity)} or {@link #on(Entity, EntityAdjunct)} is needed to construct most links. */ + public ConfigTransformer includeLinks(UriBuilder ub, boolean includeContextLinks, boolean includeActionLinks) { + this.ub = ub; + this.includeContextLinks = includeContextLinks; + this.includeActionLinks = includeActionLinks; + return this; + } + + public ConfigTransformer uiMetadata(String label, Double priority, Boolean pinned) { + this.label = label; + this.priority = priority; + this.pinned = pinned; + return this; + } + + public ConfigTransformer uiMetadata(String label, Boolean pinned) { + return uiMetadata(label, Boolean.TRUE.equals(pinned) ? 1.0d : 0, pinned); + } + + public ConfigTransformer uiIncrementAndSetPriorityIfPinned(AtomicInteger lastPriority) { + if (Boolean.TRUE.equals(pinned)) { + this.priority = (double) lastPriority.incrementAndGet(); + } + return this; + } + + public ConfigTransformer uiMetadata(Field keyField) { + if (keyField==null) return this; + return uiMetadata(keyField.getDeclaredAnnotation(CatalogConfig.class)); + } + + public ConfigTransformer uiMetadata(CatalogConfig annotation) { + if (annotation==null) return this; + return uiMetadata(annotation.label(), annotation.priority(), annotation.pinned()); + } + + public ConfigSummary transform() { + MutableMap.Builder lb = new MutableMap.Builder(); + + if (ub!=null && entity!=null) { + URI self; + if (adjunct!=null) { + self = serviceUriBuilder(ub, AdjunctApi.class, "getConfig").build(entity.getApplicationId(), entity.getId(), adjunct.getId(), key.getName()); + } else { + self = serviceUriBuilder(ub, EntityConfigApi.class, "get").build(entity.getApplicationId(), entity.getId(), key.getName()); + } + lb.put("self", self); + + if (includeContextLinks) { + // TODO wasteful including these + lb.put("application", EntityTransformer.applicationUri(entity.getApplication(), ub) ); + lb.put("entity", EntityTransformer.entityUri(entity, ub) ); + if (adjunct!=null) { + lb.put("adjunct", AdjunctTransformer.adjunctUri(entity, adjunct, ub) ); + } + } + if (includeActionLinks) { + // TODO is this json or a display value? + lb.put("action:json", self); + + Iterable hints = Iterables.filter(RendererHints.getHintsFor(key), RendererHints.NamedAction.class); + BrooklynObject context = adjunct!=null ? adjunct : entity; + for (RendererHints.NamedAction na : hints) { + SensorTransformer.addNamedAction(lb, na, context.getConfig(key), key, context); + } + } + + } + + // TODO if ui metadata not set try to infer or get more info from caller ? + + return new ConfigSummary(key, label, priority, pinned, lb.build()); + } + + @Deprecated + public org.apache.brooklyn.rest.domain.EntityConfigSummary transformLegacyEntityConfig() { + ConfigSummary v2 = transform(); + return new org.apache.brooklyn.rest.domain.EntityConfigSummary(key, v2.getLabel(), v2.getPriority(), v2.isPinned(), v2.getLinks()); + } + + @Deprecated + public org.apache.brooklyn.rest.domain.EnricherConfigSummary transformLegacyEnricherConfig() { + ConfigSummary v2 = transform(); + return new org.apache.brooklyn.rest.domain.EnricherConfigSummary(key, v2.getLabel(), v2.getPriority(), v2.getLinks()); + } + + @Deprecated + public org.apache.brooklyn.rest.domain.PolicyConfigSummary transformLegacyPolicyConfig() { + ConfigSummary v2 = transform(); + return new org.apache.brooklyn.rest.domain.PolicyConfigSummary(key, v2.getLabel(), v2.getPriority(), v2.getLinks()); + } + +} 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 73b2831eef..1537f07164 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,26 +32,27 @@ 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; import org.apache.brooklyn.rest.api.EntityApi; -import org.apache.brooklyn.rest.api.EntityConfigApi; import org.apache.brooklyn.rest.domain.ConfigSummary; 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.util.collections.MutableMap; +import org.apache.brooklyn.rest.util.BrooklynRestResourceUtils; +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 +85,7 @@ public static EntitySummary entitySummary(Entity entity, UriBuilder ub) { .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")) @@ -120,68 +122,40 @@ public EntitySummary apply(Entity entity) { })); } - public static EntityConfigSummary entityConfigSummary(ConfigKey config, String label, Double priority, Boolean pinned, Map links) { - Map mapOfLinks = links==null ? null : ImmutableMap.copyOf(links); - return new EntityConfigSummary(config, label, priority, pinned, mapOfLinks); + public static URI applicationUri(Application entity, UriBuilder ub) { + return serviceUriBuilder(ub, ApplicationApi.class, "get").build(entity.getApplicationId()); } - - public static ConfigSummary configSummary(ConfigKey config, String label, Double priority, Boolean pinned, Map links) { - return new ConfigSummary(config, label, priority, pinned, links); + + public static URI entityUri(Entity entity, UriBuilder ub) { + return serviceUriBuilder(ub, EntityApi.class, "get").build(entity.getApplicationId(), entity.getId()); + } + + /** @deprecated since 0.13.0 use {@link ConfigTransformer} */ + @Deprecated + public static EntityConfigSummary entityConfigSummary(ConfigKey config, String label, Double priority, Boolean pinned, Map links) { + return new EntityConfigSummary(config, label, priority, pinned, links); } + /** @deprecated since 0.13.0 use {@link ConfigTransformer} */ + @Deprecated public static PolicyConfigSummary policyConfigSummary(ConfigKey config, String label, Double priority, Map links) { return new PolicyConfigSummary(config, label, priority, links); } + /** @deprecated since 0.13.0 use {@link ConfigTransformer} */ + @Deprecated public static EnricherConfigSummary enricherConfigSummary(ConfigKey config, String label, Double priority, Map links) { return new EnricherConfigSummary(config, label, priority, links); } - /** generates a representation for a given config key, - * with label inferred from annoation in the entity class, - * and links pointing to the entity and the applicaiton */ - public static EntityConfigSummary entityConfigSummary(Entity entity, ConfigKey config, UriBuilder ub) { - /* - * following code nearly there to get the @CatalogConfig annotation - * in the class and use that to populate a label - */ - -// EntityDynamicType typeMap = -// ((AbstractEntity)entity).getMutableEntityType(); -// // above line works if we can cast; line below won't work, but there should some way -// // to get back the handle to the spec from an entity local, which then *would* work -// EntityTypes.getDefinedEntityType(entity.getClass()); - -// String label = typeMap.getConfigKeyField(config.getName()); - String label = null; - Double priority = null; - - URI applicationUri = serviceUriBuilder(ub, ApplicationApi.class, "get").build(entity.getApplicationId()); - URI entityUri = serviceUriBuilder(ub, EntityApi.class, "get").build(entity.getApplicationId(), entity.getId()); - URI selfUri = serviceUriBuilder(ub, EntityConfigApi.class, "get").build(entity.getApplicationId(), entity.getId(), config.getName()); - - MutableMap.Builder lb = MutableMap.builder() - .put("self", selfUri) - .put("application", applicationUri) - .put("entity", entityUri) - .put("action:json", selfUri); - - Iterable hints = Iterables.filter(RendererHints.getHintsFor(config), RendererHints.NamedAction.class); - for (RendererHints.NamedAction na : hints) { - SensorTransformer.addNamedAction(lb, na, entity.getConfig(config), config, entity); - } - - return entityConfigSummary(config, label, priority, null, lb.build()); + /** @deprecated since 0.13.0 use {@link ConfigTransformer} */ + @Deprecated + public static ConfigSummary entityConfigSummary(Entity entity, ConfigKey config, UriBuilder ub) { + return ConfigTransformer.of(config).on(entity).includeLinks(ub, true, true).transform(); } - public static URI applicationUri(Application entity, UriBuilder ub) { - return serviceUriBuilder(ub, ApplicationApi.class, "get").build(entity.getApplicationId()); - } - - public static URI entityUri(Entity entity, UriBuilder ub) { - return serviceUriBuilder(ub, EntityApi.class, "get").build(entity.getApplicationId(), entity.getId()); - } - + /** @deprecated since 0.13.0 use {@link ConfigTransformer} */ + @Deprecated public static EntityConfigSummary entityConfigSummary(ConfigKey config, Field configKeyField) { CatalogConfig catalogConfig = configKeyField!=null ? configKeyField.getAnnotation(CatalogConfig.class) : null; String label = catalogConfig==null ? null : catalogConfig.label(); @@ -190,6 +164,8 @@ public static EntityConfigSummary entityConfigSummary(ConfigKey config, Field return entityConfigSummary(config, label, priority, pinned, null); } + /** @deprecated since 0.13.0 use {@link ConfigTransformer} */ + @Deprecated public static EntityConfigSummary entityConfigSummary(SpecParameter input, AtomicInteger paramPriorityCnt) { // Increment the priority because the config container is a set. Server-side we are using an ordered set // which results in correctly ordered items on the wire (as a list). Clients which use the java bindings @@ -198,19 +174,32 @@ public static EntityConfigSummary entityConfigSummary(SpecParameter input, At return entityConfigSummary(input.getConfigKey(), input.getLabel(), priority, input.isPinned(), null); } - public static ConfigSummary configSummary(SpecParameter input) { - // could increment priority, or take from annotation, or introduce new field - Double priority = input.isPinned() ? Double.valueOf(1d) : null; - return configSummary(input.getConfigKey(), input.getLabel(), priority, input.isPinned(), null); - } - + /** @deprecated since 0.13.0 use {@link ConfigTransformer} */ + @Deprecated public static PolicyConfigSummary policyConfigSummary(SpecParameter input) { Double priority = input.isPinned() ? Double.valueOf(1d) : null; return policyConfigSummary(input.getConfigKey(), input.getLabel(), priority, null); } + /** @deprecated since 0.13.0 use {@link ConfigTransformer} */ + @Deprecated public static EnricherConfigSummary enricherConfigSummary(SpecParameter input) { Double priority = input.isPinned() ? Double.valueOf(1d) : null; return enricherConfigSummary(input.getConfigKey(), input.getLabel(), priority, null); } + + public static Map 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 source = ConfigBag.newInstance( + ((BrooklynObjectInternal)obj).config().getInternalConfigMap().getAllConfigInheritedRawValuesIgnoringErrors() ).getAllConfig(); + Map result = Maps.newLinkedHashMap(); + for (Map.Entry ek : source.entrySet()) { + result.put(ek.getKey(), utils.getStringValueForDisplay(ek.getValue())); + } + return result; + } } 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 d6ba1d33fe..577e4f1aaa 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.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 @@ /** * Converts from Brooklyn entities to restful API summary objects + * @deprecated since 0.13.0 use {@link AdjunctTransformer} and {@link AdjunctSummary} */ +@Deprecated public class PolicyTransformer { public static PolicySummary policySummary(Entity entity, Policy policy, UriBuilder ub) { diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/SensorTransformer.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/SensorTransformer.java index 19820d0f97..e4971ff80d 100644 --- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/SensorTransformer.java +++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/SensorTransformer.java @@ -18,25 +18,28 @@ */ package org.apache.brooklyn.rest.transform; +import static org.apache.brooklyn.rest.util.WebResourceUtils.serviceUriBuilder; + import java.net.URI; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import javax.ws.rs.core.UriBuilder; + import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.api.objs.BrooklynObject; import org.apache.brooklyn.api.sensor.AttributeSensor; import org.apache.brooklyn.api.sensor.Sensor; import org.apache.brooklyn.core.config.render.RendererHints; +import org.apache.brooklyn.rest.api.ApplicationApi; +import org.apache.brooklyn.rest.api.EntityApi; +import org.apache.brooklyn.rest.api.SensorApi; import org.apache.brooklyn.rest.domain.SensorSummary; import org.apache.brooklyn.util.collections.MutableMap; 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.collect.Iterables; -import javax.ws.rs.core.UriBuilder; -import org.apache.brooklyn.rest.api.ApplicationApi; -import org.apache.brooklyn.rest.api.EntityApi; -import org.apache.brooklyn.rest.api.SensorApi; -import static org.apache.brooklyn.rest.util.WebResourceUtils.serviceUriBuilder; public class SensorTransformer { @@ -71,7 +74,7 @@ private static void addNamedAction(MutableMap.Builder lb, Rende } @SuppressWarnings("unchecked") - static void addNamedAction(MutableMap.Builder lb, RendererHints.NamedAction na, T value, Object context, Entity contextEntity) { + static void addNamedAction(MutableMap.Builder lb, RendererHints.NamedAction na, T value, Object contextKeyOrSensor, BrooklynObject contextObject) { if (na instanceof RendererHints.NamedActionWithUrl) { try { String v = ((RendererHints.NamedActionWithUrl) na).getUrlFromValue(value); @@ -81,7 +84,7 @@ static void addNamedAction(MutableMap.Builder lb, RendererHints } } catch (Exception e) { Exceptions.propagateIfFatal(e); - log.warn("Unable to make action "+na+" from "+context+" on "+contextEntity+": "+e, e); + log.warn("Unable to make action "+na+" from "+contextKeyOrSensor+" on "+contextObject+": "+e, e); } } } diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/TypeTransformer.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/TypeTransformer.java index e37ee6abc1..736ee83911 100644 --- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/TypeTransformer.java +++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/TypeTransformer.java @@ -54,7 +54,6 @@ import org.apache.brooklyn.rest.domain.BundleSummary; import org.apache.brooklyn.rest.domain.ConfigSummary; import org.apache.brooklyn.rest.domain.EffectorSummary; -import org.apache.brooklyn.rest.domain.EntityConfigSummary; import org.apache.brooklyn.rest.domain.SensorSummary; import org.apache.brooklyn.rest.domain.SummaryComparators; import org.apache.brooklyn.rest.domain.TypeDetail; @@ -99,8 +98,9 @@ private static T embellish(T result, RegisteredType item Set config = Sets.newLinkedHashSet(); AbstractBrooklynObjectSpec spec = b.getTypeRegistry().createSpec(item, null, null); + AtomicInteger priority = new AtomicInteger(0); for (final SpecParameter input : spec.getParameters()){ - config.add(EntityTransformer.configSummary(input)); + config.add(ConfigTransformer.of(input).uiIncrementAndSetPriorityIfPinned(priority).transform()); } result.setExtraField("config", config); @@ -119,7 +119,7 @@ private static T embellish(T result, RegisteredType item protected static void embellishEntity(T result, RegisteredType item, BrooklynRestResourceUtils b) { try { - Set config = Sets.newLinkedHashSet(); + Set config = Sets.newLinkedHashSet(); Set sensors = Sets.newTreeSet(SummaryComparators.nameComparator()); Set effectors = Sets.newTreeSet(SummaryComparators.nameComparator()); @@ -127,9 +127,9 @@ protected static void embellishEntity(T result, Register EntityDynamicType typeMap = BrooklynTypes.getDefinedEntityType(spec.getType()); EntityType type = typeMap.getSnapshot(); - AtomicInteger paramPriorityCnt = new AtomicInteger(); + AtomicInteger priority = new AtomicInteger(); for (SpecParameter input: spec.getParameters()) - config.add(EntityTransformer.entityConfigSummary(input, paramPriorityCnt)); + config.add(ConfigTransformer.of(input).uiIncrementAndSetPriorityIfPinned(priority).transform()); for (Sensor x: type.getSensors()) sensors.add(SensorTransformer.sensorSummaryForCatalog(x)); for (Effector x: type.getEffectors()) 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 9e1ce53670..d382205b0e 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.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 LocationRegistry getLocationRegistry() { 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. + *

+ * + * @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,37 @@ public Policy getPolicy(Entity entity, String policy) { 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)}. + *

+ * + * @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 (Enricher p: entity.enrichers()) { + if (adjunct.equals(p.getId())) return p; + } + for (Feed p: ((EntityInternal)entity).feeds()) { + 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.getDisplayName())) 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 *

diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/AdjunctResourceTest.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/AdjunctResourceTest.java new file mode 100644 index 0000000000..2bd9f2d622 --- /dev/null +++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/AdjunctResourceTest.java @@ -0,0 +1,198 @@ +/* + * 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 org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.fail; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.ws.rs.core.GenericType; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.apache.brooklyn.api.objs.HighlightTuple; +import org.apache.brooklyn.rest.domain.AdjunctDetail; +import org.apache.brooklyn.rest.domain.AdjunctSummary; +import org.apache.brooklyn.rest.domain.ApplicationSpec; +import org.apache.brooklyn.rest.domain.ConfigSummary; +import org.apache.brooklyn.rest.domain.EntitySpec; +import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest; +import org.apache.brooklyn.rest.testing.mocks.RestMockSimpleEntity; +import org.apache.brooklyn.rest.testing.mocks.RestMockSimplePolicy; +import org.apache.brooklyn.test.Asserts; +import org.apache.brooklyn.util.collections.MutableList; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; + +@Test(singleThreaded = true, + // by using a different suite name we disallow interleaving other tests between the methods of this test class, which wrecks the test fixtures + suiteName = "AdjunctResourceTest") +public class AdjunctResourceTest extends BrooklynRestResourceTest { + + private static final Logger log = LoggerFactory.getLogger(AdjunctResourceTest.class); + + private static final String ENDPOINT = "/applications/simple-app/entities/simple-ent/adjuncts/"; + + private final ApplicationSpec simpleSpec = ApplicationSpec.builder().name("simple-app").entities( + ImmutableSet.of(new EntitySpec("simple-ent", RestMockSimpleEntity.class.getName()))).locations( + ImmutableSet.of("localhost")).build(); + + private String policyId; + + @BeforeClass(alwaysRun = true) + public void setUp() throws Exception { + startServer(); + Response aResponse = clientDeploy(simpleSpec); + waitForApplicationToBeRunning(aResponse.getLocation()); + + Response pResponse = client().path(ENDPOINT) + .query("type", RestMockSimplePolicy.class.getCanonicalName()) + .type(MediaType.APPLICATION_JSON_TYPE) + .post(toJsonEntity(ImmutableMap.of())); + + AdjunctDetail response = pResponse.readEntity(AdjunctDetail.class); + assertNotNull(response.getId()); + policyId = response.getId(); + + } + + @Test + public void testListAdjuncts() throws Exception { + Set adjuncts = client().path(ENDPOINT) + .get(new GenericType>() {}); + + AdjunctSummary policy = null; + List others = MutableList.of(); + for (AdjunctSummary adj : adjuncts) { + if (adj.getId().equals(policyId)) { + policy = adj; + } else { + others.add(adj); + } + } + + log.info("Non-policy adjuncts: "+others); + Asserts.assertSize(others, 4); + + assertEquals(policy.getName(), RestMockSimplePolicy.class.getName()); + } + + + @Test + public void testGetDetail() throws Exception { + AdjunctDetail policy = client().path(ENDPOINT+policyId) + .get(AdjunctDetail.class); + + assertEquals(policy.getName(), RestMockSimplePolicy.class.getName()); + } + + @Test + public void testListConfig() throws Exception { + Set config = client().path(ENDPOINT + policyId + "/config") + .get(new GenericType>() {}); + + Set configNames = Sets.newLinkedHashSet(); + for (ConfigSummary conf : config) { + configNames.add(conf.getName()); + } + + assertEquals(configNames, ImmutableSet.of( + RestMockSimplePolicy.SAMPLE_CONFIG.getName(), + RestMockSimplePolicy.INTEGER_CONFIG.getName())); + } + + @Test + public void testGetNonExistentConfigReturns404() throws Exception { + String invalidConfigName = "doesnotexist"; + try { + ConfigSummary summary = client().path(ENDPOINT + policyId + "/config/" + invalidConfigName) + .get(ConfigSummary.class); + fail("Should have thrown 404, but got "+summary); + } catch (Exception e) { + if (!e.toString().contains("404")) throw e; + } + } + + @Test + public void testGetDefaultValue() throws Exception { + String configName = RestMockSimplePolicy.SAMPLE_CONFIG.getName(); + String expectedVal = RestMockSimplePolicy.SAMPLE_CONFIG.getDefaultValue(); + + String configVal = client().path(ENDPOINT + policyId + "/config/" + configName) + .get(String.class); + assertEquals(configVal, expectedVal); + } + + @Test(dependsOnMethods = "testGetDefaultValue") + public void testReconfigureConfig() throws Exception { + String configName = RestMockSimplePolicy.SAMPLE_CONFIG.getName(); + + Response response = client().path(ENDPOINT + policyId + "/config/" + configName) + .post(toJsonEntity("newval")); + + assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); + } + + @Test(dependsOnMethods = "testReconfigureConfig") + public void testGetConfigValue() throws Exception { + String configName = RestMockSimplePolicy.SAMPLE_CONFIG.getName(); + String expectedVal = "newval"; + + Map allState = client().path(ENDPOINT + policyId + "/config-current") + .get(new GenericType>() {}); + assertEquals(allState, ImmutableMap.of(configName, expectedVal)); + + String configVal = client().path(ENDPOINT + policyId + "/config/" + configName) + .get(String.class); + assertEquals(configVal, expectedVal); + } + + @Test + public void testHighlights() throws Exception { + Set policies = client().path(ENDPOINT) + .query("adjunctType", "policy") + .get(new GenericType>() {}); + + assertEquals(policies.size(), 1); + AdjunctSummary policySummary = policies.iterator().next(); + + Map highlights = policySummary.getHighlights(); + + assertEquals(highlights.size(), 2); + HighlightTuple highlightTupleTask = highlights.get("testNameTask"); + assertEquals(highlightTupleTask.getDescription(), "testDescription"); + assertEquals(highlightTupleTask.getTime(), 123L); + assertEquals(highlightTupleTask.getTaskId(), "testTaskId"); + + HighlightTuple highlightTupleNoTask = highlights.get("testNameNoTask"); + assertEquals(highlightTupleNoTask.getDescription(), "testDescription"); + assertEquals(highlightTupleNoTask.getTime(), 123L); + assertEquals(highlightTupleNoTask.getTaskId(), null); + } +} diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestResourceTest.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestResourceTest.java index 30d6697ede..183f3de9fd 100644 --- a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestResourceTest.java +++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestResourceTest.java @@ -152,7 +152,7 @@ public Boolean call() throws Exception { .run(); if (!started) { - log.warn("Did not start application "+applicationRef+":"); + log.warn("Did not start application "+applicationRef+" ("+getApplicationStatus(applicationRef)+"):"); Collection apps = getManagementContext().getApplications(); for (Application app: apps) Entities.dumpInfo(app); diff --git a/server-cli/src/main/java/org/apache/brooklyn/cli/lister/ItemDescriptors.java b/server-cli/src/main/java/org/apache/brooklyn/cli/lister/ItemDescriptors.java index f245d06822..1a4ad31208 100644 --- a/server-cli/src/main/java/org/apache/brooklyn/cli/lister/ItemDescriptors.java +++ b/server-cli/src/main/java/org/apache/brooklyn/cli/lister/ItemDescriptors.java @@ -18,13 +18,13 @@ */ package org.apache.brooklyn.cli.lister; -import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; import org.apache.brooklyn.api.catalog.BrooklynCatalog; import org.apache.brooklyn.api.catalog.Catalog; @@ -41,12 +41,12 @@ import org.apache.brooklyn.core.entity.BrooklynConfigKeys; import org.apache.brooklyn.core.objs.BrooklynDynamicType; import org.apache.brooklyn.core.objs.BrooklynTypes; +import org.apache.brooklyn.rest.domain.ConfigSummary; import org.apache.brooklyn.rest.domain.EffectorSummary; -import org.apache.brooklyn.rest.domain.EntityConfigSummary; import org.apache.brooklyn.rest.domain.SensorSummary; import org.apache.brooklyn.rest.domain.SummaryComparators; +import org.apache.brooklyn.rest.transform.ConfigTransformer; import org.apache.brooklyn.rest.transform.EffectorTransformer; -import org.apache.brooklyn.rest.transform.EntityTransformer; import org.apache.brooklyn.rest.transform.SensorTransformer; import org.apache.brooklyn.util.collections.MutableMap; import org.apache.brooklyn.util.exceptions.RuntimeInterruptedException; @@ -131,12 +131,12 @@ public static Map toItemDescriptor(Class config = Sets.newTreeSet(SummaryComparators.nameComparator()); + Set config = Sets.newTreeSet(SummaryComparators.nameComparator()); Set sensors = Sets.newTreeSet(SummaryComparators.nameComparator()); Set effectors = Sets.newTreeSet(SummaryComparators.nameComparator()); for (ConfigKey x: type.getConfigKeys()) { - config.add(EntityTransformer.entityConfigSummary(x, dynamicType.getConfigKeyField(x.getName()))); + config.add(ConfigTransformer.of(x).uiMetadata(dynamicType.getConfigKeyField(x.getName())).transform()); } result.put("config", config); @@ -184,7 +184,7 @@ public static Object toItemDescriptor(LocationResolver resolver) { public static Map toItemDescriptor(BrooklynCatalog catalog, CatalogItem item, boolean headingsOnly) { Map itemDescriptor = MutableMap.of(); AbstractBrooklynObjectSpec spec = catalog.peekSpec(item); - List config = new ArrayList<>(); + List config = new ArrayList<>(); if (item.getDisplayName() != null) { itemDescriptor.put("name", item.getDisplayName()); @@ -199,19 +199,9 @@ public static Map toItemDescriptor(BrooklynCatalog catalog, Cata itemDescriptor.put("iconUrl", blankIfNull(item.getIconUrl())); if (!headingsOnly) { - double priorityCounter = 0.0d; + AtomicInteger priority = new AtomicInteger(0); for (SpecParameter param: spec.getParameters()) { - Double priority; - if (param.isPinned()) { - priority = priorityCounter; - priorityCounter++; - } else { - priority = null; - } - - EntityConfigSummary entityConfigSummary = EntityTransformer.entityConfigSummary(param.getConfigKey(), - param.getLabel(), priority, param.isPinned(), MutableMap.of()); - config.add(entityConfigSummary); + config.add(ConfigTransformer.of(param).uiIncrementAndSetPriorityIfPinned(priority).transform()); } itemDescriptor.put("config", config); }