You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2017/10/03 14:23:40 UTC

[03/35] brooklyn-server git commit: add adjunct REST API, deprecating policy endpoint

add adjunct REST API, deprecating policy endpoint


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/45261c7e
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/45261c7e
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/45261c7e

Branch: refs/heads/master
Commit: 45261c7e155e1022cd172d9816a647ba35f0218c
Parents: 2860af5
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Wed Sep 13 16:22:56 2017 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Wed Sep 13 17:34:53 2017 +0100

----------------------------------------------------------------------
 .../org/apache/brooklyn/api/sensor/Feed.java    |   2 +-
 .../brooklyn/core/entity/AbstractEntity.java    |   5 +
 .../brooklyn/core/entity/EntityInternal.java    |   2 +-
 .../apache/brooklyn/core/policy/Policies.java   |  30 ++-
 .../apache/brooklyn/rest/api/AdjunctApi.java    | 233 ++++++++++++++++
 .../org/apache/brooklyn/rest/api/PolicyApi.java |   2 +
 .../brooklyn/rest/api/PolicyConfigApi.java      |  27 +-
 .../brooklyn/rest/domain/AdjunctDetail.java     |  72 +++++
 .../brooklyn/rest/domain/AdjunctSummary.java    | 150 +++++++++++
 .../rest/domain/CatalogEnricherSummary.java     |   2 +
 .../rest/domain/CatalogEntitySummary.java       |   2 +
 .../rest/domain/CatalogItemSummary.java         |   2 +
 .../rest/domain/CatalogLocationSummary.java     |   2 +
 .../rest/domain/CatalogPolicySummary.java       |   2 +
 .../rest/domain/EnricherConfigSummary.java      |   3 +-
 .../rest/domain/PolicyConfigSummary.java        |   3 +-
 .../brooklyn/rest/domain/PolicySummary.java     |  83 +-----
 .../org/apache/brooklyn/rest/domain/Status.java |   7 +-
 .../apache/brooklyn/rest/BrooklynRestApi.java   |   2 +
 .../rest/resources/AdjunctResource.java         | 263 +++++++++++++++++++
 .../rest/resources/PolicyConfigResource.java    |   1 +
 .../brooklyn/rest/resources/PolicyResource.java |   1 +
 .../rest/transform/AdjunctTransformer.java      | 117 +++++++++
 .../rest/transform/EntityTransformer.java       |  21 ++
 .../rest/transform/PolicyTransformer.java       |   3 +
 .../rest/util/BrooklynRestResourceUtils.java    |  43 +++
 26 files changed, 983 insertions(+), 97 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/api/src/main/java/org/apache/brooklyn/api/sensor/Feed.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/brooklyn/api/sensor/Feed.java b/api/src/main/java/org/apache/brooklyn/api/sensor/Feed.java
index d50e092..01a4714 100644
--- a/api/src/main/java/org/apache/brooklyn/api/sensor/Feed.java
+++ b/api/src/main/java/org/apache/brooklyn/api/sensor/Feed.java
@@ -43,7 +43,7 @@ public interface Feed extends EntityAdjunct, Rebindable {
 
     /** 
      * True if everything has been _started_ (or it is starting) but not stopped,
-     * even if it is suspended; see also {@link #isActive()}
+     * even if it is suspended; see also {@link #isRunning()}
      */
     boolean isActivated();
     

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java b/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
index 08b6591..8c31512 100644
--- a/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
+++ b/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
@@ -1951,6 +1951,11 @@ public abstract class AbstractEntity extends AbstractBrooklynObject implements E
             }
             return changed;
         }
+
+        @Override
+        public Iterator<Feed> iterator() {
+            return getFeeds().iterator();
+        }
     }
     
     // -------- SENSORS --------------------

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/core/src/main/java/org/apache/brooklyn/core/entity/EntityInternal.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/EntityInternal.java b/core/src/main/java/org/apache/brooklyn/core/entity/EntityInternal.java
index d6d51a1..7377f38 100644
--- a/core/src/main/java/org/apache/brooklyn/core/entity/EntityInternal.java
+++ b/core/src/main/java/org/apache/brooklyn/core/entity/EntityInternal.java
@@ -189,7 +189,7 @@ public interface EntityInternal extends BrooklynObjectInternal, EntityLocal, Reb
         void remove(AttributeSensor<?> attribute);
     }
 
-    public interface FeedSupport {
+    public interface FeedSupport extends Iterable<Feed> {
 
         Collection<Feed> getFeeds();
 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/core/src/main/java/org/apache/brooklyn/core/policy/Policies.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/policy/Policies.java b/core/src/main/java/org/apache/brooklyn/core/policy/Policies.java
index db9bece..4b32dfc 100644
--- a/core/src/main/java/org/apache/brooklyn/core/policy/Policies.java
+++ b/core/src/main/java/org/apache/brooklyn/core/policy/Policies.java
@@ -20,7 +20,9 @@ package org.apache.brooklyn.core.policy;
 
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.EntityLocal;
+import org.apache.brooklyn.api.objs.EntityAdjunct;
 import org.apache.brooklyn.api.policy.Policy;
+import org.apache.brooklyn.api.sensor.Feed;
 import org.apache.brooklyn.api.sensor.Sensor;
 import org.apache.brooklyn.api.sensor.SensorEvent;
 import org.apache.brooklyn.api.sensor.SensorEventListener;
@@ -28,6 +30,8 @@ import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.annotations.Beta;
+
 import groovy.lang.Closure;
 
 @SuppressWarnings({"rawtypes","unchecked"})
@@ -81,11 +85,27 @@ public class Policies {
     }
 
     public static Lifecycle getPolicyStatus(Policy p) {
-        if (p.isRunning()) return Lifecycle.RUNNING;
-        if (p.isDestroyed()) return Lifecycle.DESTROYED;
-        if (p.isSuspended()) return Lifecycle.STOPPED;
-        // TODO could policy be in an error state?
-        return Lifecycle.CREATED;        
+        return inferAdjunctStatus(p);
+    }
+    
+    @Beta
+    public static Lifecycle inferAdjunctStatus(EntityAdjunct a) {
+        if (a.isRunning()) return Lifecycle.RUNNING;
+        if (a.isDestroyed()) return Lifecycle.DESTROYED;
+        
+        // adjuncts don't currently support an "error" state; though that would be useful!
+        
+        if (a instanceof Policy) {
+            if (((Policy)a).isSuspended()) return Lifecycle.STOPPED;
+            return Lifecycle.CREATED;
+        }
+        if (a instanceof Feed) {
+            if (((Feed)a).isSuspended()) return Lifecycle.STOPPED;
+            if (((Feed)a).isActivated()) return Lifecycle.STARTING;
+            return Lifecycle.CREATED;
+        }
+        
+        return Lifecycle.STOPPED;
     }
     
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/AdjunctApi.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/AdjunctApi.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/AdjunctApi.java
new file mode 100644
index 0000000..5fc1366
--- /dev/null
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/AdjunctApi.java
@@ -0,0 +1,233 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.rest.api;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.apache.brooklyn.rest.domain.AdjunctConfigSummary;
+import org.apache.brooklyn.rest.domain.AdjunctDetail;
+import org.apache.brooklyn.rest.domain.AdjunctSummary;
+import org.apache.brooklyn.rest.domain.Status;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+
+@Path("/applications/{application}/entities/{entity}/adjuncts")
+@Api("Entity Adjuncts (policies, enrichers, feeds)")
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public interface AdjunctApi {
+
+    @GET
+    @ApiOperation(value = "Fetch the adjuncts attached to a specific application entity",
+            response = org.apache.brooklyn.rest.domain.AdjunctSummary.class,
+            responseContainer = "List")
+    @ApiResponses(value = {
+            @ApiResponse(code = 404, message = "Could not find application or entity")
+    })
+    public List<AdjunctSummary> list(
+            @ApiParam(value = "Application ID or name", required = true)
+            @PathParam("application") final String application,
+            @ApiParam(value = "Entity ID or name", required = true)
+            @PathParam("entity") final String entityToken,
+            @ApiParam(value = "Filter by adjunct type", required = false)
+            @QueryParam("adjunctType") final String adjunctType);
+
+    @POST
+    @ApiOperation(value = "Add an adjunct (policy, enricher, or feed)", notes = "Returns a summary of the added adjunct")
+    @ApiResponses(value = {
+            @ApiResponse(code = 404, message = "Could not find application or entity"),
+            @ApiResponse(code = 400, message = "Type is not a suitable adjunct")
+    })
+    public AdjunctSummary addAdjunct(
+            @ApiParam(name = "application", value = "Application ID or name", required = true)
+            @PathParam("application") String application,
+
+            @ApiParam(name = "entity", value = "Entity ID or name", required = true)
+            @PathParam("entity") String entityToken,
+
+            @ApiParam(name = "type", value = "Adjunct from the type registry to instantiate and add", required = true)
+            @QueryParam("type")
+            String adjunctRegisteredTypeName,
+
+            // TODO would like to make this optional but jersey complains if we do
+            @ApiParam(name = "config", value = "Configuration for the adjunct (as key value pairs)", required = true)
+            Map<String, String> config);
+
+    @GET
+    @Path("/{adjunct}")
+    @ApiOperation(value = "Gets detail of an adjunct")
+    @ApiResponses(value = {
+            @ApiResponse(code = 404, message = "Could not find application, entity or adjunct")
+    })
+    public AdjunctDetail get(
+            @ApiParam(name = "application", value = "Application ID or name", required = true)
+            @PathParam("application") String application,
+
+            @ApiParam(name = "entity", value = "Entity ID or name", required = true)
+            @PathParam("entity") String entityToken,
+
+            @ApiParam(name = "adjunct", value = "Adjunct ID or name", required = true)
+            @PathParam("adjunct") String adjunctId);
+    
+    @GET
+    @Path("/{adjunct}/status")
+    @ApiOperation(value = "Gets status of an adjunct (RUNNING / SUSPENDED)")
+    @ApiResponses(value = {
+            @ApiResponse(code = 404, message = "Could not find application, entity or adjunct")
+    })
+    public Status getStatus(
+            @ApiParam(name = "application", value = "Application ID or name", required = true)
+            @PathParam("application") String application,
+
+            @ApiParam(name = "entity", value = "Entity ID or name", required = true)
+            @PathParam("entity") String entityToken,
+
+            @ApiParam(name = "adjunct", value = "Adjunct ID or name", required = true)
+            @PathParam("adjunct") String adjunctId);
+
+    @POST
+    @Path("/{adjunct}/start")
+    @ApiOperation(value = "Start or resume an adjunct")
+    @ApiResponses(value = {
+            @ApiResponse(code = 404, message = "Could not find application, entity or adjunct")
+    })
+    public Response start(
+            @ApiParam(name = "application", value = "Application ID or name", required = true)
+            @PathParam("application") String application,
+
+            @ApiParam(name = "entity", value = "Entity ID or name", required = true)
+            @PathParam("entity") String entityToken,
+
+            @ApiParam(name = "adjunct", value = "Adjunct ID or name", required = true)
+            @PathParam("adjunct") String adjunctId);
+
+    @POST
+    @Path("/{adjunct}/stop")
+    @ApiOperation(value = "Suspends an adjunct")
+    @ApiResponses(value = {
+            @ApiResponse(code = 404, message = "Could not find application, entity or adjunct")
+    })
+    public Response stop(
+            @ApiParam(name = "application", value = "Application ID or name", required = true)
+            @PathParam("application") String application,
+
+            @ApiParam(name = "entity", value = "Entity ID or name", required = true)
+            @PathParam("entity") String entityToken,
+
+            @ApiParam(name = "adjunct", value = "Adjunct ID or name", required = true)
+            @PathParam("adjunct") String adjunctId);
+
+    @DELETE
+    @Path("/{adjunct}")
+    @ApiOperation(value = "Destroy an adjunct", notes="Removes an adjunct from being associated with the entity and destroys it (stopping first if running)")
+    @ApiResponses(value = {
+            @ApiResponse(code = 404, message = "Could not find application, entity or adjunct")
+    })
+    public Response destroy(
+            @ApiParam(name = "application", value = "Application ID or name", required = true)
+            @PathParam("application") String application,
+
+            @ApiParam(name = "entity", value = "Entity ID or name", required = true)
+            @PathParam("entity") String entityToken,
+
+            @ApiParam(name = "adjunct", value = "Adjunct ID or name", required = true)
+            @PathParam("adjunct") String adjunctId);
+
+    /// ---------- config ---------------
+    
+    @GET
+    @Path("/{adjunct}/config")
+    @ApiOperation(value = "Fetch the config keys for a specific adjunct",
+            response = org.apache.brooklyn.rest.domain.AdjunctConfigSummary.class,
+            responseContainer = "List")
+    @ApiResponses(value = {
+            @ApiResponse(code = 404, message = "Could not find application or entity or adjunct")
+    })
+    public List<AdjunctConfigSummary> listConfig(
+            @ApiParam(value = "Application ID or name", required = true)
+            @PathParam("application") final String application,
+            @ApiParam(value = "Entity ID or name", required = true)
+            @PathParam("entity") final String entityToken,
+            @ApiParam(value = "Adjunct ID or name", required = true)
+            @PathParam("adjunct") final String adjunctToken);
+
+    // TODO support parameters  ?show=value,summary&name=xxx &format={string,json,xml}
+    // (and in sensors class)
+    @GET
+    @Path("/{adjunct}/config-current")
+    @ApiOperation(value = "Fetch config key values in batch", notes="Returns a map of config name to value")
+    public Map<String, Object> batchConfigRead(
+            @ApiParam(value = "Application ID or name", required = true)
+            @PathParam("application") String application,
+            @ApiParam(value = "Entity ID or name", required = true)
+            @PathParam("entity") String entityToken,
+            @ApiParam(value = "Adjunct ID or name", required = true)
+            @PathParam("adjunct") String adjunctToken) ;
+
+    @GET
+    @Path("/{adjunct}/config/{config}")
+    @ApiOperation(value = "Fetch config value", response = Object.class)
+    @ApiResponses(value = {
+            @ApiResponse(code = 404, message = "Could not find application, entity, adjunct or config key")
+    })
+    public String getConfig(
+            @ApiParam(value = "Application ID or name", required = true)
+            @PathParam("application") String application,
+            @ApiParam(value = "Entity ID or name", required = true)
+            @PathParam("entity") String entityToken,
+            @ApiParam(value = "Adjunct ID or name", required = true)
+            @PathParam("adjunct") String adjunctToken,
+            @ApiParam(value = "Config key ID", required = true)
+            @PathParam("config") String configKeyName);
+
+    @POST
+    @Path("/{adjunct}/config/{config}")
+    @Consumes(value = {"*/*"})
+    @ApiOperation(value = "Sets the given config on this adjunct")
+    @ApiResponses(value = {
+            @ApiResponse(code = 404, message = "Could not find application, entity, adjunct or config key")
+    })
+    public Response setConfig(
+            @ApiParam(value = "Application ID or name", required = true)
+            @PathParam("application") String application,
+            @ApiParam(value = "Entity ID or name", required = true)
+            @PathParam("entity") String entityToken,
+            @ApiParam(value = "Adjunct ID or name", required = true)
+            @PathParam("adjunct") String adjunctToken,
+            @ApiParam(value = "Config key ID", required = true)
+            @PathParam("config") String configKeyName,
+            @ApiParam(name = "value", value = "New value for the configuration", required = true)
+            Object value);
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/PolicyApi.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/PolicyApi.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/PolicyApi.java
index a698f7d..329c0e1 100644
--- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/PolicyApi.java
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/PolicyApi.java
@@ -36,6 +36,8 @@ import java.util.Map;
 @Api("Entity Policies")
 @Produces(MediaType.APPLICATION_JSON)
 @Consumes(MediaType.APPLICATION_JSON)
+/** @deprecated since 0.12.0 use AdjunctApi */
+@Deprecated
 public interface PolicyApi {
     
     @GET

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/PolicyConfigApi.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/PolicyConfigApi.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/PolicyConfigApi.java
index 1e6bfe6..06a4b86 100644
--- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/PolicyConfigApi.java
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/PolicyConfigApi.java
@@ -18,23 +18,32 @@
  */
 package org.apache.brooklyn.rest.api;
 
-import io.swagger.annotations.Api;
-import org.apache.brooklyn.rest.domain.PolicyConfigSummary;
-import io.swagger.annotations.ApiResponse;
-import io.swagger.annotations.ApiResponses;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
+import java.util.List;
+import java.util.Map;
 
-import javax.ws.rs.*;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
-import java.util.List;
-import java.util.Map;
+
+import org.apache.brooklyn.rest.domain.PolicyConfigSummary;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
 
 @Path("/applications/{application}/entities/{entity}/policies/{policy}/config")
 @Api("Entity Policy Config")
 @Produces(MediaType.APPLICATION_JSON)
 @Consumes(MediaType.APPLICATION_JSON)
+/** @deprecated since 0.12.0 use AdjunctApi */
+@Deprecated
 public interface PolicyConfigApi {
 
     @GET

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/AdjunctDetail.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/AdjunctDetail.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/AdjunctDetail.java
new file mode 100644
index 0000000..ddce5f4
--- /dev/null
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/AdjunctDetail.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.rest.domain;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.brooklyn.api.objs.EntityAdjunct;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.collections.MutableSet;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+
+public class AdjunctDetail extends AdjunctSummary {
+
+    private static final long serialVersionUID = -5086680835225136768L;
+
+    @JsonInclude(Include.NON_EMPTY)
+    private String functionallyUniqueIdentifier;
+    @JsonInclude(Include.NON_EMPTY)
+    private Set<Object> tags;
+    @JsonInclude(Include.NON_EMPTY)
+    final Set<AdjunctConfigSummary> parameters = MutableSet.of();
+    final Map<String,Object> config = MutableMap.of();
+
+    // for json
+    protected AdjunctDetail() {}
+
+    public AdjunctDetail(EntityAdjunct a) {
+        super(a);
+        this.functionallyUniqueIdentifier = a.getUniqueTag();
+        this.tags = a.tags().getTags();
+    }
+    
+    public String getFunctionallyUniqueIdentifier() {
+        return functionallyUniqueIdentifier;
+    }
+    
+    public Set<Object> getTags() {
+        return tags;
+    }
+    
+    public AdjunctDetail parameter(AdjunctConfigSummary p) {
+        parameters.add(p); return this;
+    }
+
+    public AdjunctDetail config(String key, Object val) {
+        config.put(key, val); return this;
+    }
+
+    public AdjunctDetail config(Map<String,Object> vals) {
+        config.putAll(vals); return this;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/AdjunctSummary.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/AdjunctSummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/AdjunctSummary.java
new file mode 100644
index 0000000..ca80644
--- /dev/null
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/AdjunctSummary.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.rest.domain;
+
+import java.io.Serializable;
+import java.net.URI;
+import java.util.Map;
+import java.util.Objects;
+
+import org.apache.brooklyn.api.objs.BrooklynObjectType;
+import org.apache.brooklyn.api.objs.EntityAdjunct;
+import org.apache.brooklyn.api.objs.HighlightTuple;
+import org.apache.brooklyn.api.objs.Identifiable;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.google.common.collect.ImmutableMap;
+
+public class AdjunctSummary implements HasName, Serializable, Identifiable {
+
+    private static final long serialVersionUID = -8106551648118942612L;
+    
+    private String id;
+    private String name;
+    private BrooklynObjectType adjunctType;
+    @JsonInclude(Include.NON_EMPTY)
+    private String catalogItemId;
+    private Status state;
+    @JsonInclude(Include.NON_EMPTY)
+    private Map<String, HighlightTuple> highlights;
+    
+    private Map<String, URI> links;
+
+    // for json
+    protected AdjunctSummary() {}
+    
+    public AdjunctSummary(EntityAdjunct a) {
+        id = a.getId();
+        name = a.getDisplayName();
+        adjunctType = BrooklynObjectType.of(a);
+        catalogItemId = a.getCatalogItemId();
+        highlights = a.getHighlights();
+    }
+        
+    /** @deprecated since 0.12.0 only for legacy type-specific summary classes */
+    @Deprecated   
+    protected AdjunctSummary(
+            String id,
+            String name,
+            BrooklynObjectType adjunctType,
+            String catalogItemId,
+            Status state,
+            Map<String, HighlightTuple> highlights,
+            Map<String, URI> links) {
+        this.id = id;
+        this.name = name;
+        this.adjunctType = adjunctType;
+        this.catalogItemId = catalogItemId;
+        this.state = state;
+        this.highlights = (highlights == null) ? ImmutableMap.of() : ImmutableMap.copyOf(highlights);
+        this.links = (links == null) ? ImmutableMap.<String, URI> of() : ImmutableMap.copyOf(links);
+    }
+
+    @Override
+    public String getId() {
+        return id;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    public BrooklynObjectType getAdjunctType() {
+        return adjunctType;
+    }
+    
+    public String getCatalogItemId() {
+        return catalogItemId;
+    }
+
+    public Status getState() {
+        return state;
+    }
+
+    public Map<String, HighlightTuple> getHighlights() {
+        return highlights;
+    }
+
+    public Map<String, URI> getLinks() {
+        return links;
+    }
+    
+    public AdjunctSummary state(Status state) {
+        this.state = state; return this;
+    }
+    
+    public AdjunctSummary links(Map<String, URI> links) {
+        this.links = links; return this;
+    }
+
+    
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof AdjunctSummary)) return false;
+        AdjunctSummary that = (AdjunctSummary) o;
+        return Objects.equals(id, that.id) &&
+                Objects.equals(name, that.name) &&
+                Objects.equals(adjunctType, that.adjunctType) &&
+                Objects.equals(catalogItemId, that.catalogItemId) &&
+                Objects.equals(state, that.state) &&
+                Objects.equals(highlights, that.highlights) &&
+                Objects.equals(links, that.links) ;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(id, name, adjunctType, catalogItemId, state, highlights, links);
+    }
+
+    @Override
+    public String toString() {
+        return (adjunctType!=null ? adjunctType.name() : "AdjunctSummary")+"{" +
+                "id='" + id + '\'' +
+                ", name='" + name + '\'' +
+                ", catalogItemId='" + catalogItemId + '\'' +
+                ", state='" + state + '\'' +
+                ", highlights=" + highlights +
+                ", links=" + links +
+                '}';
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogEnricherSummary.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogEnricherSummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogEnricherSummary.java
index 2e81ae1..c655515 100644
--- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogEnricherSummary.java
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogEnricherSummary.java
@@ -27,6 +27,8 @@ import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 import com.google.common.collect.ImmutableSet;
 
+/** @deprecated since 0.12.0 new TypeApi returns TypeSummary */
+@Deprecated
 public class CatalogEnricherSummary extends CatalogItemSummary {
 
     private static final long serialVersionUID = -588856488327394445L;

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogEntitySummary.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogEntitySummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogEntitySummary.java
index 5947b7f..7dbc1be 100644
--- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogEntitySummary.java
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogEntitySummary.java
@@ -27,6 +27,8 @@ import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 
+/** @deprecated since 0.12.0 new TypeApi returns TypeSummary */
+@Deprecated
 public class CatalogEntitySummary extends CatalogItemSummary {
 
     private static final long serialVersionUID = 1063908984191424539L;

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogItemSummary.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogItemSummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogItemSummary.java
index cdb86d4..cd85c13 100644
--- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogItemSummary.java
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogItemSummary.java
@@ -36,6 +36,8 @@ import com.google.common.collect.ImmutableMap;
  * see also, subclasses */
 @JsonIgnoreProperties(ignoreUnknown = true)
 // ignore unknown, ie properties from subclasses (entity)
+/** @deprecated since 0.12.0 new TypeApi returns TypeSummary */
+@Deprecated
 public class CatalogItemSummary implements HasId, HasName, Serializable {
 
     private static final long serialVersionUID = -823483595879417681L;

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogLocationSummary.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogLocationSummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogLocationSummary.java
index 0213a0c..5fb1a4b 100644
--- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogLocationSummary.java
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogLocationSummary.java
@@ -26,6 +26,8 @@ import java.util.Set;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.google.common.collect.ImmutableSet;
 
+/** @deprecated since 0.12.0 new TypeApi returns TypeSummary */
+@Deprecated
 public class CatalogLocationSummary extends CatalogItemSummary {
 
     private static final long serialVersionUID = 8420991584336514673L;

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogPolicySummary.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogPolicySummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogPolicySummary.java
index 37e5786..ad67786 100644
--- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogPolicySummary.java
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/CatalogPolicySummary.java
@@ -27,6 +27,8 @@ import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 import com.google.common.collect.ImmutableSet;
 
+/** @deprecated since 0.12.0 new TypeApi returns TypeSummary */
+@Deprecated
 public class CatalogPolicySummary extends CatalogItemSummary {
 
     private static final long serialVersionUID = -588856488327394445L;

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EnricherConfigSummary.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EnricherConfigSummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EnricherConfigSummary.java
index c868fb8..7b40206 100644
--- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EnricherConfigSummary.java
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EnricherConfigSummary.java
@@ -23,7 +23,8 @@ import java.util.Map;
 
 import org.apache.brooklyn.config.ConfigKey;
 
-// TODO remove? this class has no value over its super
+/** @deprecated since 0.12.0 new {@link AdjunctConfigSummary}; this class does nothing additional */
+@Deprecated
 public class EnricherConfigSummary extends AdjunctConfigSummary {
 
     private static final long serialVersionUID = 4339330833863794513L;

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/PolicyConfigSummary.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/PolicyConfigSummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/PolicyConfigSummary.java
index 5dfb898..797bd0c 100644
--- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/PolicyConfigSummary.java
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/PolicyConfigSummary.java
@@ -23,7 +23,8 @@ import java.util.Map;
 
 import org.apache.brooklyn.config.ConfigKey;
 
-//TODO remove? this class has no value over its super
+/** @deprecated since 0.12.0 new {@link AdjunctConfigSummary}; this class does nothing additional */
+@Deprecated
 public class PolicyConfigSummary extends AdjunctConfigSummary {
 
     private static final long serialVersionUID = 4339330833863794513L;

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/PolicySummary.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/PolicySummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/PolicySummary.java
index ce9c9e0..da4a86a 100644
--- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/PolicySummary.java
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/PolicySummary.java
@@ -18,29 +18,20 @@
  */
 package org.apache.brooklyn.rest.domain;
 
-import java.io.Serializable;
 import java.net.URI;
 import java.util.Map;
-import java.util.Objects;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-import com.google.common.collect.ImmutableMap;
 
+import org.apache.brooklyn.api.objs.BrooklynObjectType;
 import org.apache.brooklyn.api.objs.HighlightTuple;
 
-public class PolicySummary implements HasName, HasId, Serializable {
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/** @deprecated since 0.12.0 use {@link AdjunctSummary}; this class is identical */
+@Deprecated
+public class PolicySummary extends AdjunctSummary {
 
     private static final long serialVersionUID = -5086680835225136768L;
 
-    private final String id;
-    private final String name;
-    @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
-    private final String catalogItemId;
-    private final Status state;
-    private final Map<String, URI> links;
-    private final Map<String, HighlightTuple> highlights;
-
     public PolicySummary(
             @JsonProperty("id") String id,
             @JsonProperty("name") String name,
@@ -48,67 +39,7 @@ public class PolicySummary implements HasName, HasId, Serializable {
             @JsonProperty("state") Status state,
             @JsonProperty("highlights") Map<String, HighlightTuple> highlights,
             @JsonProperty("links") Map<String, URI> links) {
-        this.id = id;
-        this.name = name;
-        this.catalogItemId = catalogItemId;
-        this.state = state;
-        this.links = (links == null) ? ImmutableMap.<String, URI> of() : ImmutableMap.copyOf(links);
-        this.highlights = (highlights == null) ? ImmutableMap.of() : ImmutableMap.copyOf(highlights);
-    }
-
-    @Override
-    public String getId() {
-        return id;
-    }
-
-    @Override
-    public String getName() {
-        return name;
-    }
-
-    public String getCatalogItemId() {
-        return catalogItemId;
+        super(id, name, BrooklynObjectType.POLICY, catalogItemId, state, highlights, links);
     }
 
-    public Status getState() {
-        return state;
-    }
-
-    public Map<String, URI> getLinks() {
-        return links;
-    }
-
-    public Map<String, HighlightTuple> getHighlights() {
-        return highlights;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (!(o instanceof PolicySummary)) return false;
-        PolicySummary that = (PolicySummary) o;
-        return Objects.equals(id, that.id) &&
-                Objects.equals(name, that.name) &&
-                Objects.equals(catalogItemId, that.catalogItemId) &&
-                state == that.state &&
-                Objects.equals(highlights, that.highlights) &&
-                Objects.equals(links, that.links) ;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(id, name, catalogItemId, state, highlights, links);
-    }
-
-    @Override
-    public String toString() {
-        return "PolicySummary{" +
-                "id='" + id + '\'' +
-                ", name='" + name + '\'' +
-                ", catalogItemId='" + catalogItemId + '\'' +
-                ", state=" + state +
-                ", highlights=" + highlights +
-                ", links=" + links +
-                '}';
-    }
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/Status.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/Status.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/Status.java
index e2b2ce4..4a998c7 100644
--- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/Status.java
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/Status.java
@@ -18,9 +18,10 @@
  */
 package org.apache.brooklyn.rest.domain;
 
-/**
- * @author Adam Lowe
- */
+import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
+
+/** Canonical set, similar to {@link Lifecycle}, but cleaned up for outside consumption
+ * and more appropriate for adjunct types */
 public enum Status {
     ACCEPTED,
     STARTING,

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/BrooklynRestApi.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/BrooklynRestApi.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/BrooklynRestApi.java
index b3eb4e4..233d717 100644
--- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/BrooklynRestApi.java
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/BrooklynRestApi.java
@@ -24,6 +24,7 @@ import java.util.List;
 import org.apache.brooklyn.rest.resources.AbstractBrooklynRestResource;
 import org.apache.brooklyn.rest.resources.AccessResource;
 import org.apache.brooklyn.rest.resources.ActivityResource;
+import org.apache.brooklyn.rest.resources.AdjunctResource;
 import org.apache.brooklyn.rest.resources.ApidocResource;
 import org.apache.brooklyn.rest.resources.ApplicationResource;
 import org.apache.brooklyn.rest.resources.BundleResource;
@@ -63,6 +64,7 @@ public class BrooklynRestApi {
         resources.add(new EntityConfigResource());
         resources.add(new SensorResource());
         resources.add(new EffectorResource());
+        resources.add(new AdjunctResource());
         resources.add(new PolicyResource());
         resources.add(new PolicyConfigResource());
         resources.add(new ActivityResource());

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/AdjunctResource.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/AdjunctResource.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/AdjunctResource.java
new file mode 100644
index 0000000..9dd4c10
--- /dev/null
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/AdjunctResource.java
@@ -0,0 +1,263 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.rest.resources;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.internal.AbstractBrooklynObjectSpec;
+import org.apache.brooklyn.api.objs.BrooklynObjectType;
+import org.apache.brooklyn.api.objs.EntityAdjunct;
+import org.apache.brooklyn.api.policy.Policy;
+import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.api.sensor.Enricher;
+import org.apache.brooklyn.api.sensor.EnricherSpec;
+import org.apache.brooklyn.api.sensor.Feed;
+import org.apache.brooklyn.api.typereg.RegisteredType;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigPredicates;
+import org.apache.brooklyn.core.entity.EntityInternal;
+import org.apache.brooklyn.core.mgmt.entitlement.Entitlements;
+import org.apache.brooklyn.rest.api.AdjunctApi;
+import org.apache.brooklyn.rest.domain.AdjunctConfigSummary;
+import org.apache.brooklyn.rest.domain.AdjunctDetail;
+import org.apache.brooklyn.rest.domain.AdjunctSummary;
+import org.apache.brooklyn.rest.domain.Status;
+import org.apache.brooklyn.rest.domain.SummaryComparators;
+import org.apache.brooklyn.rest.filter.HaHotStateRequired;
+import org.apache.brooklyn.rest.transform.AdjunctTransformer;
+import org.apache.brooklyn.rest.transform.EntityTransformer;
+import org.apache.brooklyn.rest.util.BrooklynRestResourceUtils;
+import org.apache.brooklyn.rest.util.WebResourceUtils;
+import org.apache.brooklyn.util.core.ClassLoaderUtils;
+import org.apache.brooklyn.util.core.flags.TypeCoercions;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.text.Strings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicates;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+@HaHotStateRequired
+public class AdjunctResource extends AbstractBrooklynRestResource implements AdjunctApi {
+
+    private static final Logger log = LoggerFactory.getLogger(AdjunctResource.class);
+
+    private @Context UriInfo ui;
+
+    @Override
+    public List<AdjunctSummary> list(String application, String entityToken, String adjunctType) {
+        final Entity entity = brooklyn().getEntity(application, entityToken);
+        Iterable<? extends EntityAdjunct> source = Collections.emptyList();
+        boolean all = Strings.isBlank(adjunctType);
+        boolean any = false;
+        if (all || adjunctType.equalsIgnoreCase(BrooklynObjectType.POLICY.name())) {
+            any = true;
+            source = Iterables.concat(source, entity.policies());
+        }
+        if (all || adjunctType.equalsIgnoreCase(BrooklynObjectType.ENRICHER.name())) {
+            any = true;
+            source = Iterables.concat(source, entity.enrichers());
+        }
+        if (all || adjunctType.equalsIgnoreCase(BrooklynObjectType.FEED.name())) {
+            any = true;
+            source = Iterables.concat(source, ((EntityInternal)entity).feeds());
+        }
+        if (!any) {
+            throw WebResourceUtils.badRequest("Unknown adjunct type '%s'; use 'policy', 'enricher', or 'feed'", adjunctType);
+        }
+        return FluentIterable.from(source)
+            .transform(new Function<EntityAdjunct, AdjunctSummary>() {
+                @Override
+                public AdjunctSummary apply(EntityAdjunct adjunct) {
+                    return AdjunctTransformer.adjunctSummary(entity, adjunct, ui.getBaseUriBuilder());
+                }
+            })
+            .toSortedList(SummaryComparators.nameComparator());
+    }
+
+    // TODO would like to make 'config' arg optional but jersey complains if we do
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    @Override
+    public AdjunctSummary addAdjunct(String application, String entityToken, String adjunctTypeName, Map<String, String> config) {
+        Entity entity = brooklyn().getEntity(application, entityToken);
+        if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.MODIFY_ENTITY, entity)) {
+            throw WebResourceUtils.forbidden("User '%s' is not authorized to modify entity '%s'",
+                    Entitlements.getEntitlementContext().user(), entity);
+        }
+        
+        RegisteredType rt = brooklyn().getTypeRegistry().get(adjunctTypeName);
+        AbstractBrooklynObjectSpec<?, ?> spec;
+        if (rt!=null) {
+            spec = brooklyn().getTypeRegistry().createSpec(rt, null, null);
+        } else {
+            try {
+                Class<?> type = new ClassLoaderUtils(this, mgmt()).loadClass(adjunctTypeName);
+                if (Policy.class.isAssignableFrom(type)) spec = PolicySpec.create((Class) type);
+                else if (Enricher.class.isAssignableFrom(type)) spec = EnricherSpec.create((Class) type);
+                else if (Feed.class.isAssignableFrom(type)) {
+                    // TODO add FeedSpec ?  would be needed even if using the type registry
+                    throw WebResourceUtils.badRequest("Creation of feeds from java type (%s) not supported", adjunctTypeName);
+                } else {
+                    throw WebResourceUtils.badRequest("Invalid type %s; not a support adjunct type", adjunctTypeName);
+                }
+            } catch (ClassNotFoundException e) {
+                throw WebResourceUtils.badRequest("No adjunct with type %s found", adjunctTypeName);
+            } catch (ClassCastException e) {
+                throw WebResourceUtils.badRequest("No adjunct with type %s found", adjunctTypeName);
+            } catch (Exception e) {
+                throw Exceptions.propagate(e);
+            }
+        }
+
+        spec.configure(config);
+
+        EntityAdjunct instance;
+        if (spec instanceof PolicySpec) instance = entity.policies().add((PolicySpec)spec);
+        else if (spec instanceof EnricherSpec) instance = entity.enrichers().add((EnricherSpec)spec);
+        else {
+            // TODO add FeedSpec
+            throw WebResourceUtils.badRequest("Unexpected spec type %s", spec);
+        }
+
+        log.debug("REST API added adjunct " + instance + " to " + entity);
+
+        return AdjunctTransformer.adjunctDetail(brooklyn(), entity, instance, ui.getBaseUriBuilder());
+    }
+
+    @Override
+    public AdjunctDetail get(String application, String entityToken, String adjunctId) {
+        Entity entity = brooklyn().getEntity(application, entityToken);
+        EntityAdjunct adjunct = brooklyn().getAdjunct(entity, adjunctId);
+
+        return AdjunctTransformer.adjunctDetail(brooklyn(), entity, adjunct, ui.getBaseUriBuilder());
+    }
+    
+    @Override
+    public Status getStatus(String application, String entityToken, String adjunctId) {
+        return AdjunctTransformer.inferStatus( brooklyn().getAdjunct(application, entityToken, adjunctId) );
+    }
+
+    @Override
+    public Response start(String application, String entityToken, String adjunctId) {
+        EntityAdjunct adjunct = brooklyn().getAdjunct(application, entityToken, adjunctId);
+        if (adjunct instanceof Policy) ((Policy)adjunct).resume();
+        else if (adjunct instanceof Feed) ((Feed)adjunct).resume();
+        else throw WebResourceUtils.badRequest("%s does not support start/resume", adjunct);
+        
+        return Response.status(Response.Status.NO_CONTENT).build();
+    }
+
+    @Override
+    public Response stop(String application, String entityToken, String adjunctId) {
+        EntityAdjunct adjunct = brooklyn().getAdjunct(application, entityToken, adjunctId);
+        if (adjunct instanceof Policy) ((Policy)adjunct).suspend();
+        else if (adjunct instanceof Feed) ((Feed)adjunct).suspend();
+        else throw WebResourceUtils.badRequest("%s does not support suspend", adjunct);
+        
+        return Response.status(Response.Status.NO_CONTENT).build();
+    }
+
+    @Override
+    public Response destroy(String application, String entityToken, String adjunctId) {
+        Entity entity = brooklyn().getEntity(application, entityToken);
+        EntityAdjunct adjunct = brooklyn().getAdjunct(entity, adjunctId);
+
+        if (adjunct instanceof Policy) {
+            ((Policy)adjunct).suspend();
+            entity.policies().remove((Policy) adjunct);
+        } else if (adjunct instanceof Enricher) {
+            entity.enrichers().remove((Enricher) adjunct);
+        } else if (adjunct instanceof Feed) {
+            ((Feed)adjunct).suspend();
+            ((EntityInternal)entity).feeds().remove((Feed) adjunct);
+        } else {
+            throw WebResourceUtils.badRequest("Unexpected adjunct type %s", adjunct);
+        }
+        
+        return Response.status(Response.Status.NO_CONTENT).build();
+    }
+    
+    // ---- config ----
+    
+    @Override
+    public List<AdjunctConfigSummary> listConfig(
+            final String application, final String entityToken, final String adjunctToken) {
+        Entity entity = brooklyn().getEntity(application, entityToken);
+        EntityAdjunct adjunct = brooklyn().getAdjunct(entity, adjunctToken);
+
+        List<AdjunctConfigSummary> result = Lists.newArrayList();
+        for (ConfigKey<?> key : adjunct.config().findKeysPresent(Predicates.alwaysTrue())) {
+            result.add(AdjunctTransformer.configSummary(brooklyn(), entity, adjunct, key, ui.getBaseUriBuilder()));
+        }
+        return result;
+    }
+
+    // TODO support parameters  ?show=value,summary&name=xxx &format={string,json,xml}
+    // (and in sensors class)
+    @Override
+    public Map<String, Object> batchConfigRead(String application, String entityToken, String adjunctToken) {
+        // TODO: add test
+        return EntityTransformer.getConfigValues(brooklyn(), brooklyn().getAdjunct(application, entityToken, adjunctToken) );
+    }
+
+    @Override
+    public String getConfig(String application, String entityToken, String adjunctToken, String configKeyName) {
+        EntityAdjunct adjunct = brooklyn().getAdjunct(application, entityToken, adjunctToken);
+        Set<ConfigKey<?>> cki = adjunct.config().findKeysDeclared(ConfigPredicates.nameSatisfies(Predicates.equalTo(configKeyName)));
+        if (cki.isEmpty()) throw WebResourceUtils.notFound("Cannot find config key '%s' in policy '%s' of entity '%s'", configKeyName, adjunctToken, entityToken);
+
+        return brooklyn().getStringValueForDisplay(adjunct.config().get(cki.iterator().next()));
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    @Override
+    public Response setConfig(String application, String entityToken, String adjunctToken, String configKeyName, Object value) {
+        Entity entity = brooklyn().getEntity(application, entityToken);
+        if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.MODIFY_ENTITY, entity)) {
+            throw WebResourceUtils.forbidden("User '%s' is not authorized to modify entity '%s'",
+                    Entitlements.getEntitlementContext().user(), entity);
+        }
+
+        EntityAdjunct adjunct = brooklyn().getAdjunct(entity, adjunctToken);
+        Set<ConfigKey<?>> cki = adjunct.config().findKeysDeclared(ConfigPredicates.nameSatisfies(Predicates.equalTo(configKeyName)));
+        if (cki.isEmpty()) throw WebResourceUtils.notFound("Cannot find config key '%s' in policy '%s' of entity '%s'", configKeyName, adjunctToken, entityToken);
+        ConfigKey<?> ck = cki.iterator().next();
+        
+        adjunct.config().set((ConfigKey) cki, TypeCoercions.coerce(value, ck.getTypeToken()));
+
+        return Response.status(Response.Status.OK).build();
+    }
+
+    public static String getStringValueForDisplay(BrooklynRestResourceUtils utils, EntityAdjunct policy, Object value) {
+        return utils.getStringValueForDisplay(value);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/PolicyConfigResource.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/PolicyConfigResource.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/PolicyConfigResource.java
index 6dcdf19..1fca535 100644
--- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/PolicyConfigResource.java
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/PolicyConfigResource.java
@@ -41,6 +41,7 @@ import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 
 @HaHotStateRequired
+@Deprecated
 public class PolicyConfigResource extends AbstractBrooklynRestResource implements PolicyConfigApi {
 
     @Override

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/PolicyResource.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/PolicyResource.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/PolicyResource.java
index 0b8d2c2..125b0b7 100644
--- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/PolicyResource.java
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/PolicyResource.java
@@ -47,6 +47,7 @@ import com.google.common.collect.FluentIterable;
 import com.google.common.collect.Maps;
 
 @HaHotStateRequired
+@Deprecated
 public class PolicyResource extends AbstractBrooklynRestResource implements PolicyApi {
 
     private static final Logger log = LoggerFactory.getLogger(PolicyResource.class);

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/AdjunctTransformer.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/AdjunctTransformer.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/AdjunctTransformer.java
new file mode 100644
index 0000000..db7d4cd
--- /dev/null
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/AdjunctTransformer.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.rest.transform;
+
+import static org.apache.brooklyn.rest.util.WebResourceUtils.serviceUriBuilder;
+
+import java.net.URI;
+import java.util.Map;
+
+import javax.ws.rs.core.UriBuilder;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.objs.EntityAdjunct;
+import org.apache.brooklyn.api.policy.Policy;
+import org.apache.brooklyn.api.sensor.Feed;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.policy.Policies;
+import org.apache.brooklyn.rest.api.AdjunctApi;
+import org.apache.brooklyn.rest.api.ApplicationApi;
+import org.apache.brooklyn.rest.api.EntityApi;
+import org.apache.brooklyn.rest.domain.AdjunctConfigSummary;
+import org.apache.brooklyn.rest.domain.AdjunctDetail;
+import org.apache.brooklyn.rest.domain.AdjunctSummary;
+import org.apache.brooklyn.rest.domain.ApplicationSummary;
+import org.apache.brooklyn.rest.domain.Status;
+import org.apache.brooklyn.rest.util.BrooklynRestResourceUtils;
+import org.apache.brooklyn.util.collections.MutableMap;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * Converts from Brooklyn entities to restful API summary objects
+ */
+public class AdjunctTransformer {
+
+    public static AdjunctSummary adjunctSummary(Entity entity, EntityAdjunct adjunct, UriBuilder ub) {
+        return embellish(new AdjunctSummary(adjunct), entity, adjunct, ub);
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <T extends AdjunctSummary> T embellish(T adjunctSummary, Entity entity, EntityAdjunct adjunct, UriBuilder ub) {
+        return (T) adjunctSummary.state(inferStatus(adjunct)).links( buildLinks(entity, adjunct, ub, adjunctSummary instanceof AdjunctDetail) );
+    }
+
+    public static AdjunctDetail adjunctDetail(BrooklynRestResourceUtils utils, Entity entity, EntityAdjunct adjunct, UriBuilder ub) {
+        AdjunctDetail result = embellish(new AdjunctDetail(adjunct), entity, adjunct, ub);
+        for (ConfigKey<?> key: adjunct.config().findKeysDeclared(Predicates.alwaysTrue())) {
+            result.parameter(configSummary(utils, entity, adjunct, key, ub));
+        }
+        result.config(EntityTransformer.getConfigValues(utils, adjunct));
+        return result;
+    }
+
+    protected static Map<String, URI> buildLinks(Entity entity, EntityAdjunct adjunct, UriBuilder ub, boolean detail) {
+        MutableMap<String,URI> links = MutableMap.of();
+
+        links.put("self", serviceUriBuilder(ub, AdjunctApi.class, "get").build(entity.getApplicationId(), entity.getId(), adjunct.getId()));
+        
+        if (detail) {
+            links.put("application", serviceUriBuilder(ub, ApplicationApi.class, "get").build(entity.getApplicationId()));
+            links.put("entity", serviceUriBuilder(ub, EntityApi.class, "get").build(entity.getApplicationId(), entity.getId()));
+            links.put("config", serviceUriBuilder(ub, AdjunctApi.class, "listConfig").build(entity.getApplicationId(), entity.getId(), adjunct.getId()));
+            links.put("status", serviceUriBuilder(ub, AdjunctApi.class, "getStatus").build(entity.getApplicationId(), entity.getId(), adjunct.getId()));
+            if (adjunct instanceof Policy || adjunct instanceof Feed) {
+                links.put("start", serviceUriBuilder(ub, AdjunctApi.class, "start").build(entity.getApplicationId(), entity.getId(), adjunct.getId()));
+                links.put("stop", serviceUriBuilder(ub, AdjunctApi.class, "stop").build(entity.getApplicationId(), entity.getId(), adjunct.getId()));
+            }
+            links.put("destroy", serviceUriBuilder(ub, AdjunctApi.class, "destroy").build(entity.getApplicationId(), entity.getId(), adjunct.getId()));
+        }
+        
+        return links.asUnmodifiable();
+    }
+
+    public static Status inferStatus(EntityAdjunct adjunct) {
+        return ApplicationTransformer.statusFromLifecycle( Policies.inferAdjunctStatus(adjunct) );
+    }
+
+    public static AdjunctConfigSummary configSummary(BrooklynRestResourceUtils utils, ApplicationSummary application, Entity entity, EntityAdjunct adjunct, ConfigKey<?> config, UriBuilder ub) {
+        return configSummary(utils, entity, adjunct, config, ub);
+    }
+
+    public static AdjunctConfigSummary configSummary(BrooklynRestResourceUtils utils, Entity entity, EntityAdjunct adjunct, ConfigKey<?> config, UriBuilder ub) {
+        URI applicationUri = serviceUriBuilder(ub, ApplicationApi.class, "get").build(entity.getApplicationId());
+        URI entityUri = serviceUriBuilder(ub, EntityApi.class, "get").build(entity.getApplicationId(), entity.getId());
+        URI adjunctUri = serviceUriBuilder(ub, AdjunctApi.class, "get").build(entity.getApplicationId(), entity.getId(), adjunct.getId());
+        URI configUri = serviceUriBuilder(ub, AdjunctApi.class, "getConfig").build(entity.getApplicationId(), entity.getId(), adjunct.getId(), config.getName());
+
+        Map<String, URI> links = ImmutableMap.<String, URI>builder()
+                .put("self", configUri)
+                .put("application", applicationUri)
+                .put("entity", entityUri)
+                .put("policy", adjunctUri)
+                .build();
+
+        return new AdjunctConfigSummary(config.getName(), config.getTypeName(), config.getDescription(), 
+                utils.getStringValueForDisplay(config.getDefaultValue()), 
+                config.isReconfigurable(), 
+                links);
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/EntityTransformer.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/EntityTransformer.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/EntityTransformer.java
index 6bf3f19..cb7877f 100644
--- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/EntityTransformer.java
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/EntityTransformer.java
@@ -32,10 +32,12 @@ import javax.ws.rs.core.UriBuilder;
 import org.apache.brooklyn.api.catalog.CatalogConfig;
 import org.apache.brooklyn.api.entity.Application;
 import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.objs.BrooklynObject;
 import org.apache.brooklyn.api.objs.SpecParameter;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.config.render.RendererHints;
+import org.apache.brooklyn.core.objs.BrooklynObjectInternal;
 import org.apache.brooklyn.core.typereg.RegisteredTypes;
 import org.apache.brooklyn.rest.api.ApplicationApi;
 import org.apache.brooklyn.rest.api.CatalogApi;
@@ -46,12 +48,15 @@ import org.apache.brooklyn.rest.domain.EnricherConfigSummary;
 import org.apache.brooklyn.rest.domain.EntityConfigSummary;
 import org.apache.brooklyn.rest.domain.EntitySummary;
 import org.apache.brooklyn.rest.domain.PolicyConfigSummary;
+import org.apache.brooklyn.rest.util.BrooklynRestResourceUtils;
 import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.core.config.ConfigBag;
 
 import com.google.common.base.Function;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
 
 /**
  * @author Adam Lowe
@@ -84,6 +89,7 @@ public class EntityTransformer {
                 .put("config", URI.create(entityUri + "/config"))
                 .put("sensors", URI.create(entityUri + "/sensors"))
                 .put("effectors", URI.create(entityUri + "/effectors"))
+                .put("adjuncts", URI.create(entityUri + "/adjuncts"))
                 .put("policies", URI.create(entityUri + "/policies"))
                 .put("activities", URI.create(entityUri + "/activities"))
                 .put("locations", URI.create(entityUri + "/locations"))
@@ -212,4 +218,19 @@ public class EntityTransformer {
         Double priority = input.isPinned() ? Double.valueOf(1d) : null;
         return enricherConfigSummary(input.getConfigKey(), input.getLabel(), priority, null);
     }
+    
+    public static Map<String, Object> getConfigValues(BrooklynRestResourceUtils utils, BrooklynObject obj) {
+        // alternatively could do this - should be the same ?
+//        for (ConfigKey<?> key: adjunct.config().findKeysPresent(Predicates.alwaysTrue())) {
+//            result.config(key.getName(), utils.getStringValueForDisplay( adjunct.config().get(key) ));
+//        }
+        
+        Map<String, Object> source = ConfigBag.newInstance(
+            ((BrooklynObjectInternal)obj).config().getInternalConfigMap().getAllConfigInheritedRawValuesIgnoringErrors() ).getAllConfig();
+        Map<String, Object> result = Maps.newLinkedHashMap();
+        for (Map.Entry<String, Object> ek : source.entrySet()) {
+            result.put(ek.getKey(), utils.getStringValueForDisplay(ek.getValue()));
+        }
+        return result;
+    }
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/PolicyTransformer.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/PolicyTransformer.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/PolicyTransformer.java
index 8dad949..294fd41 100644
--- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/PolicyTransformer.java
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/PolicyTransformer.java
@@ -25,6 +25,7 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.policy.Policies;
+import org.apache.brooklyn.rest.domain.AdjunctSummary;
 import org.apache.brooklyn.rest.domain.ApplicationSummary;
 import org.apache.brooklyn.rest.domain.PolicyConfigSummary;
 import org.apache.brooklyn.rest.domain.PolicySummary;
@@ -42,7 +43,9 @@ import static org.apache.brooklyn.rest.util.WebResourceUtils.serviceUriBuilder;
 
 /**
  * Converts from Brooklyn entities to restful API summary objects
+ * @deprecated since 0.12.0 use {@link AdjunctTransformer} and {@link AdjunctSummary} 
  */
+@Deprecated
 public class PolicyTransformer {
 
     public static PolicySummary policySummary(Entity entity, Policy policy, UriBuilder ub) {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/45261c7e/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtils.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtils.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtils.java
index 9e1ce53..9b0a3d4 100644
--- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtils.java
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtils.java
@@ -38,7 +38,10 @@ import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.LocationRegistry;
 import org.apache.brooklyn.api.mgmt.ManagementContext;
 import org.apache.brooklyn.api.mgmt.Task;
+import org.apache.brooklyn.api.objs.EntityAdjunct;
 import org.apache.brooklyn.api.policy.Policy;
+import org.apache.brooklyn.api.sensor.Enricher;
+import org.apache.brooklyn.api.sensor.Feed;
 import org.apache.brooklyn.api.typereg.BrooklynTypeRegistry;
 import org.apache.brooklyn.api.typereg.RegisteredType;
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampConstants;
@@ -112,6 +115,16 @@ public class BrooklynRestResourceUtils {
     public Policy getPolicy(String application, String entity, String policy) {
         return getPolicy(getEntity(application, entity), policy);
     }
+    
+    /** finds the policy indicated by the given ID or name.
+     * @see {@link #getEntity(String,String)}; it then searches the policies of that
+     * entity for one whose ID or name matches that given.
+     * <p>
+     * 
+     * @throws 404 or 412 (unless input is null in which case output is null) */
+    public EntityAdjunct getAdjunct(String application, String entity, String adjunct) {
+        return getAdjunct(getEntity(application, entity), adjunct);
+    }
 
     /** finds the policy indicated by the given ID or name.
      * @see {@link #getPolicy(String,String,String)}.
@@ -130,6 +143,36 @@ public class BrooklynRestResourceUtils {
         
         throw WebResourceUtils.notFound("Cannot find policy '%s' in entity '%s'", policy, entity);
     }
+    
+    /** finds the policy indicated by the given ID or name.
+     * @see {@link #getAdjunct(String,String,String)}.
+     * <p>
+     * 
+     * @throws 404 or 412 (unless input is null in which case output is null) */
+    public EntityAdjunct getAdjunct(Entity entity, String adjunct) {
+        if (adjunct==null) return null;
+
+        for (Policy p: entity.policies()) {
+            if (adjunct.equals(p.getId())) return p;
+        }
+        for (Policy p: entity.policies()) {
+            if (adjunct.equals(p.getDisplayName())) return p;
+        }
+        for (Enricher p: entity.enrichers()) {
+            if (adjunct.equals(p.getId())) return p;
+        }
+        for (Enricher p: entity.enrichers()) {
+            if (adjunct.equals(p.getDisplayName())) return p;
+        }
+        for (Feed p: ((EntityInternal)entity).feeds()) {
+            if (adjunct.equals(p.getId())) return p;
+        }
+        for (Feed p: ((EntityInternal)entity).feeds()) {
+            if (adjunct.equals(p.getDisplayName())) return p;
+        }
+        
+        throw WebResourceUtils.notFound("Cannot find adjunct '%s' in entity '%s'", adjunct, entity);
+    }
 
     /** finds the entity indicated by the given ID or name
      * <p>