http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/DefaultExceptionMapper.java
----------------------------------------------------------------------
diff --git 
a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/DefaultExceptionMapper.java
 
b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/DefaultExceptionMapper.java
new file mode 100644
index 0000000..25b3f39
--- /dev/null
+++ 
b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/DefaultExceptionMapper.java
@@ -0,0 +1,102 @@
+/*
+ * 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.util;
+
+import java.util.Set;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.yaml.snakeyaml.error.YAMLException;
+
+import brooklyn.management.entitlement.Entitlements;
+import brooklyn.rest.domain.ApiError;
+import brooklyn.rest.domain.ApiError.Builder;
+import brooklyn.util.collections.MutableSet;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.exceptions.UserFacingException;
+import brooklyn.util.flags.ClassCoercionException;
+import brooklyn.util.text.Strings;
+
+@Provider
+public class DefaultExceptionMapper implements ExceptionMapper<Throwable> {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(DefaultExceptionMapper.class);
+
+    static Set<Class<?>> warnedUnknownExceptions = MutableSet.of();
+    
+    /**
+     * Maps a throwable to a response.
+     * <p/>
+     * Returns {@link WebApplicationException#getResponse} if the exception is 
an instance of
+     * {@link WebApplicationException}. Otherwise maps known exceptions to 
responses. If no
+     * mapping is found a {@link Status#INTERNAL_SERVER_ERROR} is assumed.
+     */
+    @Override
+    public Response toResponse(Throwable throwable1) {
+
+        LOG.debug("REST request running as {} threw: {}", 
Entitlements.getEntitlementContext(), 
+            Exceptions.collapse(throwable1));
+        if (LOG.isTraceEnabled()) {
+            LOG.trace("Full details of 
"+Entitlements.getEntitlementContext()+" "+throwable1, throwable1);
+        }
+
+        Throwable throwable2 = Exceptions.getFirstInteresting(throwable1);
+        // Some methods will throw this, which gets converted automatically
+        if (throwable2 instanceof WebApplicationException) {
+            WebApplicationException wae = (WebApplicationException) throwable2;
+            return wae.getResponse();
+        }
+
+        // The nicest way for methods to provide errors, wrap as this, and the 
stack trace will be suppressed
+        if (throwable2 instanceof UserFacingException) {
+            return 
ApiError.of(throwable2.getMessage()).asBadRequestResponseJson();
+        }
+
+        // For everything else, a trace is supplied
+        
+        // Assume ClassCoercionExceptions are caused by TypeCoercions from 
input parameters gone wrong
+        // And IllegalArgumentException for malformed input parameters.
+        if (throwable2 instanceof ClassCoercionException || throwable2 
instanceof IllegalArgumentException) {
+            return ApiError.of(throwable2).asBadRequestResponseJson();
+        }
+
+        // YAML exception 
+        if (throwable2 instanceof YAMLException) {
+            return 
ApiError.builder().message(throwable2.getMessage()).prefixMessage("Invalid 
YAML").build().asBadRequestResponseJson();
+        }
+
+        if (!Exceptions.isPrefixBoring(throwable2)) {
+            if ( warnedUnknownExceptions.add( throwable2.getClass() )) {
+                LOG.warn("REST call generated exception type 
"+throwable2.getClass()+" unrecognized in "+getClass()+" (subsequent 
occurrences will be logged debug only): " + throwable2, throwable2);
+            }
+        }
+        
+        Builder rb = 
ApiError.builderFromThrowable(Exceptions.collapse(throwable2));
+        if (Strings.isBlank(rb.getMessage()))
+            rb.message("Internal error. Contact server administrator to 
consult logs for more details.");
+        return rb.build().asResponse(Status.INTERNAL_SERVER_ERROR, 
MediaType.APPLICATION_JSON_TYPE);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/EntityLocationUtils.java
----------------------------------------------------------------------
diff --git 
a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/EntityLocationUtils.java
 
b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/EntityLocationUtils.java
new file mode 100644
index 0000000..d1c02e8
--- /dev/null
+++ 
b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/EntityLocationUtils.java
@@ -0,0 +1,85 @@
+/*
+ * 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.util;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import brooklyn.entity.Entity;
+import brooklyn.location.Location;
+import brooklyn.location.basic.LocationConfigKeys;
+import brooklyn.management.ManagementContext;
+
+public class EntityLocationUtils {
+
+    protected final ManagementContext context;
+
+    public EntityLocationUtils(ManagementContext ctx) {
+        this.context = ctx;
+    }
+    
+    /* Returns the number of entites at each location for which the geographic 
coordinates are known. */
+    public Map<Location, Integer> countLeafEntitiesByLocatedLocations() {
+        Map<Location, Integer> result = new LinkedHashMap<Location, Integer>();
+        for (Entity e: context.getApplications()) {
+            countLeafEntitiesByLocatedLocations(e, null, result);
+        }
+        return result;
+    }
+
+    protected void countLeafEntitiesByLocatedLocations(Entity target, Entity 
locatedParent, Map<Location, Integer> result) {
+        if (isLocatedLocation(target))
+            locatedParent = target;
+        if (!target.getChildren().isEmpty()) {
+            // non-leaf - inspect children
+            for (Entity child: target.getChildren()) 
+                countLeafEntitiesByLocatedLocations(child, locatedParent, 
result);
+        } else {
+            // leaf node - increment location count
+            if (locatedParent!=null) {
+                for (Location l: locatedParent.getLocations()) {
+                    Location ll = getMostGeneralLocatedLocation(l);
+                    if (ll!=null) {
+                        Integer count = result.get(ll);
+                        if (count==null) count = 1;
+                        else count++;
+                        result.put(ll, count);
+                    }
+                }
+            }
+        }
+    }
+
+    protected Location getMostGeneralLocatedLocation(Location l) {
+        if (l==null) return null;
+        if (!isLocatedLocation(l)) return null;
+        Location ll = getMostGeneralLocatedLocation(l.getParent());
+        if (ll!=null) return ll;
+        return l;
+    }
+
+    protected boolean isLocatedLocation(Entity target) {
+        for (Location l: target.getLocations())
+            if (isLocatedLocation(l)) return true;
+        return false;
+    }
+    protected boolean isLocatedLocation(Location l) {
+        return l.getConfig(LocationConfigKeys.LATITUDE)!=null && 
l.getConfig(LocationConfigKeys.LONGITUDE)!=null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/FormMapProvider.java
----------------------------------------------------------------------
diff --git 
a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/FormMapProvider.java
 
b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/FormMapProvider.java
new file mode 100644
index 0000000..2b5c19b
--- /dev/null
+++ 
b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/FormMapProvider.java
@@ -0,0 +1,81 @@
+/*
+ * 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.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.List;
+import java.util.Map;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.Provider;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.sun.jersey.core.impl.provider.entity.FormMultivaluedMapProvider;
+import com.sun.jersey.core.util.MultivaluedMapImpl;
+
+/**
+ * A MessageBodyReader producing a <code>Map&lt;String, Object&gt;</code>, 
where Object
+ * is either a <code>String</code>, a <code>List&lt;String&gt;</code> or null.
+ */
+@Provider
+@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+public class FormMapProvider implements MessageBodyReader<Map<String, Object>> 
{
+
+    @Override
+    public boolean isReadable(Class<?> type, Type genericType, Annotation[] 
annotations, MediaType mediaType) {
+        if (!Map.class.equals(type) || !(genericType instanceof 
ParameterizedType)) {
+            return false;
+        }
+        ParameterizedType parameterized = (ParameterizedType) genericType;
+        return parameterized.getActualTypeArguments().length == 2 &&
+                parameterized.getActualTypeArguments()[0] == String.class &&
+                parameterized.getActualTypeArguments()[1] == Object.class;
+    }
+
+    @Override
+    public Map<String, Object> readFrom(Class<Map<String, Object>> type, Type 
genericType, Annotation[] annotations,
+            MediaType mediaType, MultivaluedMap<String, String> httpHeaders, 
InputStream entityStream)
+            throws IOException, WebApplicationException {
+        FormMultivaluedMapProvider delegate = new FormMultivaluedMapProvider();
+        MultivaluedMap<String, String> multi = new MultivaluedMapImpl();
+        multi = delegate.readFrom(multi, mediaType, entityStream);
+
+        Map<String, Object> map = 
Maps.newHashMapWithExpectedSize(multi.keySet().size());
+        for (String key : multi.keySet()) {
+            List<String> value = multi.get(key);
+            if (value.size() > 1) {
+                map.put(key, Lists.newArrayList(value));
+            } else if (value.size() == 1) {
+                map.put(key, Iterables.getOnlyElement(value));
+            } else {
+                map.put(key, null);
+            }
+        }
+        return map;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/ManagementContextProvider.java
----------------------------------------------------------------------
diff --git 
a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/ManagementContextProvider.java
 
b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/ManagementContextProvider.java
new file mode 100644
index 0000000..4b24f05
--- /dev/null
+++ 
b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/ManagementContextProvider.java
@@ -0,0 +1,33 @@
+/*
+ * 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.util;
+
+import javax.ws.rs.core.Context;
+
+import brooklyn.management.ManagementContext;
+
+import com.sun.jersey.spi.inject.SingletonTypeInjectableProvider;
+
+public class ManagementContextProvider extends 
SingletonTypeInjectableProvider<Context, ManagementContext> {
+
+    public ManagementContextProvider(ManagementContext instance) {
+        super(ManagementContext.class, instance);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/ShutdownHandler.java
----------------------------------------------------------------------
diff --git 
a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/ShutdownHandler.java
 
b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/ShutdownHandler.java
new file mode 100644
index 0000000..e573bf6
--- /dev/null
+++ 
b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/ShutdownHandler.java
@@ -0,0 +1,23 @@
+/*
+ * 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.util;
+
+public interface ShutdownHandler {
+    void onShutdownRequest();
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/ShutdownHandlerProvider.java
----------------------------------------------------------------------
diff --git 
a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/ShutdownHandlerProvider.java
 
b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/ShutdownHandlerProvider.java
new file mode 100644
index 0000000..f499ca0
--- /dev/null
+++ 
b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/ShutdownHandlerProvider.java
@@ -0,0 +1,30 @@
+/*
+ * 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.util;
+
+import javax.annotation.Nullable;
+import javax.ws.rs.core.Context;
+
+import com.sun.jersey.spi.inject.SingletonTypeInjectableProvider;
+
+public class ShutdownHandlerProvider extends 
SingletonTypeInjectableProvider<Context, ShutdownHandler> {
+    public ShutdownHandlerProvider(@Nullable ShutdownHandler instance) {
+        super(ShutdownHandler.class, instance);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/URLParamEncoder.java
----------------------------------------------------------------------
diff --git 
a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/URLParamEncoder.java
 
b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/URLParamEncoder.java
new file mode 100644
index 0000000..bfb6a3b
--- /dev/null
+++ 
b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/URLParamEncoder.java
@@ -0,0 +1,27 @@
+/*
+ * 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.util;
+
+
+/**
+ * @deprecated since 0.7.0 use {@link brooklyn.util.net.URLParamEncoder}
+ */
+public class URLParamEncoder extends brooklyn.util.net.URLParamEncoder {
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/WebResourceUtils.java
----------------------------------------------------------------------
diff --git 
a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/WebResourceUtils.java
 
b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/WebResourceUtils.java
new file mode 100644
index 0000000..67508bb
--- /dev/null
+++ 
b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/WebResourceUtils.java
@@ -0,0 +1,162 @@
+/*
+ * 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.util;
+
+import java.io.IOException;
+import java.util.Map;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.codehaus.jackson.map.ObjectMapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.catalog.internal.CatalogUtils;
+import brooklyn.rest.domain.ApiError;
+import org.apache.brooklyn.rest.util.json.BrooklynJacksonJsonProvider;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.net.Urls;
+import brooklyn.util.text.StringEscapes.JavaStringEscapes;
+
+import com.google.common.collect.ImmutableMap;
+import com.sun.jersey.spi.container.ContainerResponse;
+
+public class WebResourceUtils {
+
+    private static final Logger log = 
LoggerFactory.getLogger(WebResourceUtils.class);
+
+    /** @throws WebApplicationException with an ApiError as its body and the 
given status as its response code. */
+    public static WebApplicationException 
throwWebApplicationException(Response.Status status, String format, Object... 
args) {
+        String msg = String.format(format, args);
+        if (log.isDebugEnabled()) {
+            log.debug("responding {} {} ({})",
+                    new Object[]{status.getStatusCode(), 
status.getReasonPhrase(), msg});
+        }
+        ApiError apiError = 
ApiError.builder().message(msg).errorCode(status).build();
+        // including a Throwable is the only way to include a message with the 
WebApplicationException - ugly!
+        throw new WebApplicationException(new Throwable(apiError.toString()), 
apiError.asJsonResponse());
+    }
+
+    /** @throws WebApplicationException With code 500 internal server error */
+    public static WebApplicationException serverError(String format, Object... 
args) {
+        return 
throwWebApplicationException(Response.Status.INTERNAL_SERVER_ERROR, format, 
args);
+    }
+
+    /** @throws WebApplicationException With code 400 bad request */
+    public static WebApplicationException badRequest(String format, Object... 
args) {
+        return throwWebApplicationException(Response.Status.BAD_REQUEST, 
format, args);
+    }
+
+    /** @throws WebApplicationException With code 401 unauthorized */
+    public static WebApplicationException unauthorized(String format, 
Object... args) {
+        return throwWebApplicationException(Response.Status.UNAUTHORIZED, 
format, args);
+    }
+
+    /** @throws WebApplicationException With code 403 forbidden */
+    public static WebApplicationException forbidden(String format, Object... 
args) {
+        return throwWebApplicationException(Response.Status.FORBIDDEN, format, 
args);
+    }
+
+    /** @throws WebApplicationException With code 404 not found */
+    public static WebApplicationException notFound(String format, Object... 
args) {
+        return throwWebApplicationException(Response.Status.NOT_FOUND, format, 
args);
+    }
+
+    /** @throws WebApplicationException With code 412 precondition failed */
+    public static WebApplicationException preconditionFailed(String format, 
Object... args) {
+        return 
throwWebApplicationException(Response.Status.PRECONDITION_FAILED, format, args);
+    }
+
+    public final static Map<String,com.google.common.net.MediaType> 
IMAGE_FORMAT_MIME_TYPES = ImmutableMap.<String, 
com.google.common.net.MediaType>builder()
+            .put("jpg", com.google.common.net.MediaType.JPEG)
+            .put("jpeg", com.google.common.net.MediaType.JPEG)
+            .put("png", com.google.common.net.MediaType.PNG)
+            .put("gif", com.google.common.net.MediaType.GIF)
+            .put("svg", com.google.common.net.MediaType.SVG_UTF_8)
+            .build();
+    
+    public static MediaType getImageMediaTypeFromExtension(String extension) {
+        com.google.common.net.MediaType mime = 
IMAGE_FORMAT_MIME_TYPES.get(extension.toLowerCase());
+        if (mime==null) return null;
+        try {
+            return MediaType.valueOf(mime.toString());
+        } catch (Exception e) {
+            log.warn("Unparseable MIME type "+mime+"; ignoring ("+e+")");
+            Exceptions.propagateIfFatal(e);
+            return null;
+        }
+    }
+
+    /** as {@link #getValueForDisplay(ObjectMapper, Object, boolean, boolean)} 
with no mapper
+     * (so will only handle a subset of types) */
+    public static Object getValueForDisplay(Object value, boolean preferJson, 
boolean isJerseyReturnValue) {
+        return getValueForDisplay(null, value, preferJson, 
isJerseyReturnValue);
+    }
+    
+    /** returns an object which jersey will handle nicely, converting to json,
+     * sometimes wrapping in quotes if needed (for outermost json return 
types);
+     * if json is not preferred, this simply applies a toString-style 
rendering */ 
+    public static Object getValueForDisplay(ObjectMapper mapper, Object value, 
boolean preferJson, boolean isJerseyReturnValue) {
+        if (preferJson) {
+            if (value==null) return null;
+            Object result = value;
+            // no serialization checks required, with new smart-mapper which 
does toString
+            // (note there is more sophisticated logic in git history however)
+            result = value;
+            
+            if (isJerseyReturnValue) {
+                if (result instanceof String) {
+                    // Jersey does not do json encoding if the return type is 
a string,
+                    // expecting the returner to do the json encoding himself
+                    // cf discussion at 
https://github.com/dropwizard/dropwizard/issues/231
+                    result = JavaStringEscapes.wrapJavaString((String)result);
+                }
+            }
+            
+            return result;
+        } else {
+            if (value==null) return "";
+            return value.toString();            
+        }
+    }
+
+    public static String getPathFromVersionedId(String versionedId) {
+        if (CatalogUtils.looksLikeVersionedId(versionedId)) {
+            String symbolicName = 
CatalogUtils.getIdFromVersionedId(versionedId);
+            String version = 
CatalogUtils.getVersionFromVersionedId(versionedId);
+            return Urls.encode(symbolicName) + "/" + Urls.encode(version);
+        } else {
+            return Urls.encode(versionedId);
+        }
+    }
+
+    /** Sets the {@link HttpServletResponse} target (last argument) from the 
given source {@link Response};
+     * useful in filters where we might have a {@link Response} and need to 
set up an {@link HttpServletResponse}. 
+     * Similar to {@link ContainerResponse#setResponse(Response)}; nothing 
like that seems to be available for {@link HttpServletResponse}. */
+    public static void applyJsonResponse(ServletContext servletContext, 
Response source, HttpServletResponse target) throws IOException {
+        target.setStatus(source.getStatus());
+        target.setContentType(MediaType.APPLICATION_JSON);
+        target.setCharacterEncoding("UTF-8");
+        
target.getWriter().write(BrooklynJacksonJsonProvider.findAnyObjectMapper(servletContext,
 null).writeValueAsString(source.getEntity()));
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/BidiSerialization.java
----------------------------------------------------------------------
diff --git 
a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/BidiSerialization.java
 
b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/BidiSerialization.java
new file mode 100644
index 0000000..434962e
--- /dev/null
+++ 
b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/BidiSerialization.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.util.json;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.codehaus.jackson.JsonGenerationException;
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.JsonParser;
+import org.codehaus.jackson.JsonProcessingException;
+import org.codehaus.jackson.map.DeserializationContext;
+import org.codehaus.jackson.map.JsonDeserializer;
+import org.codehaus.jackson.map.JsonSerializer;
+import org.codehaus.jackson.map.SerializerProvider;
+import org.codehaus.jackson.map.module.SimpleModule;
+
+import brooklyn.basic.BrooklynObject;
+import brooklyn.entity.Entity;
+import brooklyn.location.Location;
+import brooklyn.management.ManagementContext;
+
+public class BidiSerialization {
+
+    protected final static ThreadLocal<Boolean> STRICT_SERIALIZATION = new 
ThreadLocal<Boolean>(); 
+
+    /**
+     * Sets strict serialization on, or off (the default), for the current 
thread.
+     * Recommended to be used in a <code>try { ... } finally { ... }</code> 
block
+     * with {@link #clearStrictSerialization()} at the end.
+     * <p>
+     * With strict serialization, classes must have public fields or annotated 
fields, else they will not be serialized.
+     */
+    public static void setStrictSerialization(Boolean value) {
+        STRICT_SERIALIZATION.set(value);
+    }
+
+    public static void clearStrictSerialization() {
+        STRICT_SERIALIZATION.remove();
+    }
+
+    public static boolean isStrictSerialization() {
+        Boolean result = STRICT_SERIALIZATION.get();
+        if (result!=null) return result;
+        return false;
+    }
+
+
+    public abstract static class AbstractWithManagementContextSerialization<T> 
{
+
+        protected class Serializer extends JsonSerializer<T> {
+            @Override
+            public void serialize(T value, JsonGenerator jgen, 
SerializerProvider provider) throws IOException, JsonProcessingException {
+                
AbstractWithManagementContextSerialization.this.serialize(value, jgen, 
provider);
+            }
+        }
+        
+        protected class Deserializer extends JsonDeserializer<T> {
+            @Override
+            public T deserialize(JsonParser jp, DeserializationContext ctxt) 
throws IOException, JsonProcessingException {
+                return 
AbstractWithManagementContextSerialization.this.deserialize(jp, ctxt);
+            }
+        }
+        
+        protected final Serializer serializer = new Serializer();
+        protected final Deserializer deserializer = new Deserializer();
+        protected final Class<T> type;
+        protected final ManagementContext mgmt;
+        
+        public AbstractWithManagementContextSerialization(Class<T> type, 
ManagementContext mgmt) {
+            this.type = type;
+            this.mgmt = mgmt;
+        }
+        
+        public JsonSerializer<T> getSerializer() {
+            return serializer;
+        }
+        
+        public JsonDeserializer<T> getDeserializer() {
+            return deserializer;
+        }
+
+        public void serialize(T value, JsonGenerator jgen, SerializerProvider 
provider) throws IOException, JsonProcessingException {
+            jgen.writeStartObject();
+            writeBody(value, jgen, provider);
+            jgen.writeEndObject();
+        }
+
+        protected void writeBody(T value, JsonGenerator jgen, 
SerializerProvider provider) throws IOException, JsonGenerationException, 
JsonProcessingException {
+            jgen.writeStringField("type", value.getClass().getCanonicalName());
+            customWriteBody(value, jgen, provider);
+        }
+
+        public abstract void customWriteBody(T value, JsonGenerator jgen, 
SerializerProvider provider) throws IOException, JsonProcessingException;
+
+        public T deserialize(JsonParser jp, DeserializationContext ctxt) 
throws IOException, JsonProcessingException {
+            @SuppressWarnings("unchecked")
+            Map<Object,Object> values = jp.readValueAs(Map.class);
+            String type = (String) values.get("type");
+            return customReadBody(type, values, jp, ctxt);
+        }
+
+        protected abstract T customReadBody(String type, Map<Object, Object> 
values, JsonParser jp, DeserializationContext ctxt) throws IOException, 
JsonProcessingException;
+
+        public void install(SimpleModule module) {
+            module.addSerializer(type, serializer);
+            module.addDeserializer(type, deserializer);
+        }
+    }
+    
+    public static class ManagementContextSerialization extends 
AbstractWithManagementContextSerialization<ManagementContext> {
+        public ManagementContextSerialization(ManagementContext mgmt) { 
super(ManagementContext.class, mgmt); }
+        @Override
+        public void customWriteBody(ManagementContext value, JsonGenerator 
jgen, SerializerProvider provider) throws IOException, JsonProcessingException 
{}
+        @Override
+        protected ManagementContext customReadBody(String type, Map<Object, 
Object> values, JsonParser jp, DeserializationContext ctxt) throws IOException, 
JsonProcessingException {
+            return mgmt;
+        }
+    }
+    
+    public abstract static class AbstractBrooklynObjectSerialization<T extends 
BrooklynObject> extends AbstractWithManagementContextSerialization<T> {
+        public AbstractBrooklynObjectSerialization(Class<T> type, 
ManagementContext mgmt) { 
+            super(type, mgmt);
+        }
+        @Override
+        protected void writeBody(T value, JsonGenerator jgen, 
SerializerProvider provider) throws IOException, JsonGenerationException, 
JsonProcessingException {
+            jgen.writeStringField("type", type.getCanonicalName());
+            customWriteBody(value, jgen, provider);
+        }
+        @Override
+        public void customWriteBody(T value, JsonGenerator jgen, 
SerializerProvider provider) throws IOException, JsonProcessingException {
+            jgen.writeStringField("id", value.getId());
+        }
+        @Override
+        protected T customReadBody(String type, Map<Object, Object> values, 
JsonParser jp, DeserializationContext ctxt) throws IOException, 
JsonProcessingException {
+            return getInstanceFromId((String) values.get("id"));
+        }
+        protected abstract T getInstanceFromId(String id);
+    }
+
+    public static class EntitySerialization extends 
AbstractBrooklynObjectSerialization<Entity> {
+        public EntitySerialization(ManagementContext mgmt) { 
super(Entity.class, mgmt); }
+        @Override protected Entity getInstanceFromId(String id) { return 
mgmt.getEntityManager().getEntity(id); }
+    }
+    public static class LocationSerialization extends 
AbstractBrooklynObjectSerialization<Location> {
+        public LocationSerialization(ManagementContext mgmt) { 
super(Location.class, mgmt); }
+        @Override protected Location getInstanceFromId(String id) { return 
mgmt.getLocationManager().getLocation(id); }
+    }
+    // TODO how to look up policies and enrichers? (not essential...)
+//    public static class PolicySerialization extends 
AbstractBrooklynObjectSerialization<Policy> {
+//        public EntitySerialization(ManagementContext mgmt) { 
super(Policy.class, mgmt); }
+//        @Override protected Policy getKind(String id) { return 
mgmt.getEntityManager().getEntity(id); }
+//    }
+//    public static class EnricherSerialization extends 
AbstractBrooklynObjectSerialization<Enricher> {
+//        public EntitySerialization(ManagementContext mgmt) { 
super(Entity.class, mgmt); }
+//        @Override protected Enricher getKind(String id) { return 
mgmt.getEntityManager().getEntity(id); }
+//    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/BrooklynJacksonJsonProvider.java
----------------------------------------------------------------------
diff --git 
a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/BrooklynJacksonJsonProvider.java
 
b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/BrooklynJacksonJsonProvider.java
new file mode 100644
index 0000000..7362c6d
--- /dev/null
+++ 
b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/BrooklynJacksonJsonProvider.java
@@ -0,0 +1,172 @@
+/*
+ * 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.util.json;
+
+import javax.servlet.ServletContext;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+
+import org.codehaus.jackson.Version;
+import org.codehaus.jackson.jaxrs.JacksonJsonProvider;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.map.SerializationConfig;
+import org.codehaus.jackson.map.module.SimpleModule;
+import org.codehaus.jackson.map.type.TypeFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.config.BrooklynProperties;
+import brooklyn.config.BrooklynServiceAttributes;
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.management.ManagementContext;
+import brooklyn.management.ManagementContextInjectable;
+
+public class BrooklynJacksonJsonProvider extends JacksonJsonProvider 
implements ManagementContextInjectable {
+
+    private static final Logger log = 
LoggerFactory.getLogger(BrooklynJacksonJsonProvider.class);
+
+    public static final String BROOKLYN_REST_OBJECT_MAPPER = 
BrooklynServiceAttributes.BROOKLYN_REST_OBJECT_MAPPER;
+
+    @Context protected ServletContext servletContext;
+
+    protected ObjectMapper ourMapper;
+    protected boolean notFound = false;
+
+    private ManagementContext mgmt;
+
+    public ObjectMapper locateMapper(Class<?> type, MediaType mediaType) {
+        if (ourMapper != null)
+            return ourMapper;
+
+        findSharedMapper();
+
+        if (ourMapper != null)
+            return ourMapper;
+
+        if (!notFound) {
+            log.warn("Management context not available; using default 
ObjectMapper in "+this);
+            notFound = true;
+        }
+
+        return super.locateMapper(Object.class, 
MediaType.APPLICATION_JSON_TYPE);
+    }
+
+    protected synchronized ObjectMapper findSharedMapper() {
+        if (ourMapper != null || notFound)
+            return ourMapper;
+
+        ourMapper = findSharedObjectMapper(servletContext, mgmt);
+        if (ourMapper == null) return null;
+
+        if (notFound) {
+            notFound = false;
+        }
+        log.debug("Found mapper "+ourMapper+" for "+this+", creating custom 
Brooklyn mapper");
+
+        return ourMapper;
+    }
+
+    /**
+     * Finds a shared {@link ObjectMapper} or makes a new one, stored against 
the servlet context;
+     * returns null if a shared instance cannot be created.
+     */
+    public static ObjectMapper findSharedObjectMapper(ServletContext 
servletContext, ManagementContext mgmt) {
+        if (servletContext != null) {
+            synchronized (servletContext) {
+                ObjectMapper mapper = (ObjectMapper) 
servletContext.getAttribute(BROOKLYN_REST_OBJECT_MAPPER);
+                if (mapper != null) return mapper;
+
+                mapper = 
newPrivateObjectMapper(getManagementContext(servletContext));
+                servletContext.setAttribute(BROOKLYN_REST_OBJECT_MAPPER, 
mapper);
+                return mapper;
+            }
+        }
+        if (mgmt != null) {
+            synchronized (mgmt) {
+                ConfigKey<ObjectMapper> key = 
ConfigKeys.newConfigKey(ObjectMapper.class, BROOKLYN_REST_OBJECT_MAPPER);
+                ObjectMapper mapper = mgmt.getConfig().getConfig(key);
+                if (mapper != null) return mapper;
+
+                mapper = newPrivateObjectMapper(mgmt);
+                log.debug("Storing new ObjectMapper against "+mgmt+" because 
no ServletContext available: "+mapper);
+                ((BrooklynProperties)mgmt.getConfig()).put(key, mapper);
+                return mapper;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Like {@link #findSharedObjectMapper(ServletContext, ManagementContext)} 
but will create a private
+     * ObjectMapper if it can, from the servlet context and/or the management 
context, or else fail
+     */
+    public static ObjectMapper findAnyObjectMapper(ServletContext 
servletContext, ManagementContext mgmt) {
+        ObjectMapper mapper = findSharedObjectMapper(servletContext, mgmt);
+        if (mapper != null) return mapper;
+
+        if (mgmt == null && servletContext != null) {
+            mgmt = getManagementContext(servletContext);
+        }
+        return newPrivateObjectMapper(mgmt);
+    }
+
+    /**
+     * @return A new Brooklyn-specific ObjectMapper.
+     *   Normally {@link #findSharedObjectMapper(ServletContext, 
ManagementContext)} is preferred
+     */
+    public static ObjectMapper newPrivateObjectMapper(ManagementContext mgmt) {
+        if (mgmt == null) {
+            throw new IllegalStateException("No management context available 
for creating ObjectMapper");
+        }
+
+        SerializationConfig defaultConfig = new 
ObjectMapper().getSerializationConfig();
+        SerializationConfig sc = new SerializationConfig(
+            defaultConfig.getClassIntrospector() /* 
ObjectMapper.DEFAULT_INTROSPECTOR */,
+            defaultConfig.getAnnotationIntrospector() /* 
ObjectMapper.DEFAULT_ANNOTATION_INTROSPECTOR */,
+            new PossiblyStrictPreferringFieldsVisibilityChecker(),
+            null, null, TypeFactory.defaultInstance(), null);
+
+        ConfigurableSerializerProvider sp = new 
ConfigurableSerializerProvider();
+        sp.setUnknownTypeSerializer(new 
ErrorAndToStringUnknownTypeSerializer());
+
+        ObjectMapper mapper = new ObjectMapper(null, sp, null, sc, null);
+        SimpleModule mapperModule = new SimpleModule("Brooklyn", new 
Version(0, 0, 0, "ignored"));
+
+        new 
BidiSerialization.ManagementContextSerialization(mgmt).install(mapperModule);
+        new BidiSerialization.EntitySerialization(mgmt).install(mapperModule);
+        new 
BidiSerialization.LocationSerialization(mgmt).install(mapperModule);
+        mapperModule.addSerializer(new MultimapSerializer());
+
+        mapper.registerModule(mapperModule);
+        return mapper;
+    }
+
+    public static ManagementContext getManagementContext(ServletContext 
servletContext) {
+        if (servletContext == null)
+            return null;
+
+        return (ManagementContext) 
servletContext.getAttribute(BrooklynServiceAttributes.BROOKLYN_MANAGEMENT_CONTEXT);
+    }
+
+    @Override
+    public void injectManagementContext(ManagementContext mgmt) {
+        this.mgmt = mgmt;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/ConfigurableSerializerProvider.java
----------------------------------------------------------------------
diff --git 
a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/ConfigurableSerializerProvider.java
 
b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/ConfigurableSerializerProvider.java
new file mode 100644
index 0000000..20078df
--- /dev/null
+++ 
b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/ConfigurableSerializerProvider.java
@@ -0,0 +1,94 @@
+/*
+ * 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.util.json;
+
+import java.io.IOException;
+
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.JsonProcessingException;
+import org.codehaus.jackson.JsonStreamContext;
+import org.codehaus.jackson.map.JsonSerializer;
+import org.codehaus.jackson.map.SerializationConfig;
+import org.codehaus.jackson.map.SerializerFactory;
+import org.codehaus.jackson.map.ser.StdSerializerProvider;
+import org.codehaus.jackson.type.JavaType;
+
+import brooklyn.util.exceptions.Exceptions;
+
+/** allows the serializer-of-last-resort to be customized, ie used for 
unknown-types */
+final class ConfigurableSerializerProvider extends StdSerializerProvider {
+    
+    public ConfigurableSerializerProvider() {}
+    
+    public ConfigurableSerializerProvider(SerializationConfig config) {
+        // NB: not usually necessary to pass config, as object mapper gets its 
own config set explicitly
+        this(config, new ConfigurableSerializerProvider(), null);
+    }
+    
+    public ConfigurableSerializerProvider(SerializationConfig config, 
ConfigurableSerializerProvider src, SerializerFactory jsf) {
+        super(config, src, jsf);
+        unknownTypeSerializer = src.unknownTypeSerializer;
+    }
+    
+    protected StdSerializerProvider createInstance(SerializationConfig config, 
SerializerFactory jsf) {
+        return new ConfigurableSerializerProvider(config, this, jsf);
+    }
+
+    protected JsonSerializer<Object> unknownTypeSerializer;
+    
+    public JsonSerializer<Object> getUnknownTypeSerializer(Class<?> 
unknownType) {
+        if (unknownTypeSerializer!=null) return unknownTypeSerializer;
+        return super.getUnknownTypeSerializer(unknownType);
+    }
+    
+    public void setUnknownTypeSerializer(JsonSerializer<Object> 
unknownTypeSerializer) {
+        this.unknownTypeSerializer = unknownTypeSerializer;
+    }
+
+    @Override
+    protected void _serializeValue(JsonGenerator jgen, Object value) throws 
IOException, JsonProcessingException {
+        JsonStreamContext ctxt = jgen.getOutputContext();
+        try {
+            super._serializeValue(jgen, value);
+        } catch (Exception e) {
+            onSerializationException(ctxt, jgen, value, e);
+        }
+    }
+
+    @Override
+    protected void _serializeValue(JsonGenerator jgen, Object value, JavaType 
rootType) throws IOException, JsonProcessingException {
+        JsonStreamContext ctxt = jgen.getOutputContext();
+        try {
+            super._serializeValue(jgen, value, rootType);
+        } catch (Exception e) {
+            onSerializationException(ctxt, jgen, value, e);
+        }
+    }
+
+    protected void onSerializationException(JsonStreamContext ctxt, 
JsonGenerator jgen, Object value, Exception e) throws IOException, 
JsonProcessingException {
+        Exceptions.propagateIfFatal(e);
+
+        JsonSerializer<Object> unknownTypeSerializer = 
getUnknownTypeSerializer(value.getClass());
+        if (unknownTypeSerializer instanceof 
ErrorAndToStringUnknownTypeSerializer) {
+            
((ErrorAndToStringUnknownTypeSerializer)unknownTypeSerializer).serializeFromError(ctxt,
 e, value, jgen, this);
+        } else {
+            unknownTypeSerializer.serialize(value, jgen, this);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/ErrorAndToStringUnknownTypeSerializer.java
----------------------------------------------------------------------
diff --git 
a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/ErrorAndToStringUnknownTypeSerializer.java
 
b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/ErrorAndToStringUnknownTypeSerializer.java
new file mode 100644
index 0000000..b865cb0
--- /dev/null
+++ 
b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/ErrorAndToStringUnknownTypeSerializer.java
@@ -0,0 +1,125 @@
+/*
+ * 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.util.json;
+
+import java.io.IOException;
+import java.io.NotSerializableException;
+import java.util.Collections;
+import java.util.Set;
+
+import javax.annotation.Nullable;
+
+import org.codehaus.jackson.JsonGenerationException;
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.JsonProcessingException;
+import org.codehaus.jackson.JsonStreamContext;
+import org.codehaus.jackson.map.JsonMappingException;
+import org.codehaus.jackson.map.SerializerProvider;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+import org.codehaus.jackson.map.ser.impl.UnknownSerializer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.util.collections.MutableSet;
+import brooklyn.util.javalang.Reflections;
+
+/**
+ * for non-json-serializable classes (quite a lot of them!) simply provide a 
sensible error message and a toString.
+ * TODO maybe we want to attempt to serialize fields instead?  (but being 
careful not to be self-referential!) 
+ */
+public class ErrorAndToStringUnknownTypeSerializer extends UnknownSerializer {
+    
+    private static final Logger log = 
LoggerFactory.getLogger(ErrorAndToStringUnknownTypeSerializer.class);
+    private static Set<String> WARNED_CLASSES = 
Collections.synchronizedSet(MutableSet.<String>of());
+    
+    @Override
+    public void serialize(Object value, JsonGenerator jgen, SerializerProvider 
provider) throws IOException, JsonProcessingException {
+        if (BidiSerialization.isStrictSerialization())
+            throw new JsonMappingException("Cannot serialize object containing 
"+value.getClass().getName()+" when strict serialization requested");
+
+        serializeFromError(jgen.getOutputContext(), null, value, jgen, 
provider);
+    }
+
+    public void serializeFromError(JsonStreamContext ctxt, @Nullable Exception 
error, Object value, JsonGenerator jgen, SerializerProvider 
configurableSerializerProvider) throws JsonGenerationException, IOException {
+        if (log.isDebugEnabled())
+            log.debug("Recovering from json serialization error, serializing 
"+value+": "+error);
+        
+        if (BidiSerialization.isStrictSerialization())
+            throw new JsonMappingException("Cannot serialize "
+                + (ctxt!=null && !ctxt.inRoot() ? "object containing " : "")
+                + value.getClass().getName()+" when strict serialization 
requested");
+        
+        if (WARNED_CLASSES.add(value.getClass().getCanonicalName())) {
+            log.warn("Standard serialization not possible for 
"+value.getClass()+" ("+value+")", error);
+        }
+        JsonStreamContext newCtxt = jgen.getOutputContext();
+
+        // very odd, but flush seems necessary when working with large 
objects; presumably a buffer which is allowed to clear itself?
+        // without this, when serializing the large (1.5M) Server json object 
from BrooklynJacksonSerializerTest creates invalid json,
+        // containing:  "foo":false,"{"error":true,...
+        jgen.flush();
+        
+        boolean createObject = !newCtxt.inObject() || 
newCtxt.getCurrentName()!=null;
+        if (createObject) {
+            jgen.writeStartObject();
+        }
+        
+        if (allowEmpty(value.getClass())) {
+            // write nothing
+        } else {
+
+            jgen.writeFieldName("error");
+            jgen.writeBoolean(true);
+
+            jgen.writeFieldName("errorType");
+            
jgen.writeString(NotSerializableException.class.getCanonicalName());
+
+            jgen.writeFieldName("type");
+            jgen.writeString(value.getClass().getCanonicalName());
+
+            jgen.writeFieldName("toString");
+            jgen.writeString(value.toString());
+
+            if (error!=null) {
+                jgen.writeFieldName("causedByError");
+                jgen.writeString(error.toString());
+            }
+            
+        }
+        
+        if (createObject) {
+            jgen.writeEndObject();
+        }
+        
+        while (newCtxt!=null && !newCtxt.equals(ctxt)) {
+            if (jgen.getOutputContext().inArray()) { jgen.writeEndArray(); 
continue; }
+            if (jgen.getOutputContext().inObject()) { jgen.writeEndObject(); 
continue; } 
+            break;
+        }
+
+    }
+
+    protected boolean allowEmpty(Class<? extends Object> clazz) {
+        if (clazz.getAnnotation(JsonSerialize.class)!=null && 
Reflections.hasNoNonObjectFields(clazz)) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/MultimapSerializer.java
----------------------------------------------------------------------
diff --git 
a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/MultimapSerializer.java
 
b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/MultimapSerializer.java
new file mode 100644
index 0000000..1c2f8c1
--- /dev/null
+++ 
b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/MultimapSerializer.java
@@ -0,0 +1,62 @@
+/*
+ * 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.util.json;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Map;
+
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.map.SerializerProvider;
+import org.codehaus.jackson.map.ser.std.SerializerBase;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+
+/**
+ * Provides a serializer for {@link Multimap} instances.
+ * <p>
+ * When Brooklyn's Jackson dependency is updated from 
org.codehaus.jackson:1.9.13 to
+ * com.fasterxml.jackson:2.3+ then this class should be replaced with a 
dependency on
+ * jackson-datatype-guava and a GuavaModule registered with Brooklyn's 
ObjectMapper.
+ */
+@Beta
+public class MultimapSerializer extends SerializerBase<Multimap<?, ?>> {
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    protected MultimapSerializer() {
+        super((Class<Multimap<?, ?>>) (Class) Multimap.class);
+    }
+
+    @Override
+    public void serialize(Multimap<?, ?> value, JsonGenerator jgen, 
SerializerProvider provider) throws IOException {
+        jgen.writeStartObject();
+        writeEntries(value, jgen, provider);
+        jgen.writeEndObject();
+    }
+
+    private void writeEntries(Multimap<?, ?> value, JsonGenerator jgen, 
SerializerProvider provider) throws IOException {
+        for (Map.Entry<?, ? extends Collection<?>> entry : 
value.asMap().entrySet()) {
+            provider.findKeySerializer(provider.constructType(String.class), 
null)
+                    .serialize(entry.getKey(), jgen, provider);
+            
provider.defaultSerializeValue(Lists.newArrayList(entry.getValue()), jgen);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/PossiblyStrictPreferringFieldsVisibilityChecker.java
----------------------------------------------------------------------
diff --git 
a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/PossiblyStrictPreferringFieldsVisibilityChecker.java
 
b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/PossiblyStrictPreferringFieldsVisibilityChecker.java
new file mode 100644
index 0000000..502f063
--- /dev/null
+++ 
b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/PossiblyStrictPreferringFieldsVisibilityChecker.java
@@ -0,0 +1,107 @@
+/*
+ * 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.util.json;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+
+import org.codehaus.jackson.annotate.JsonAutoDetect;
+import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility;
+import org.codehaus.jackson.annotate.JsonMethod;
+import org.codehaus.jackson.map.introspect.AnnotatedField;
+import org.codehaus.jackson.map.introspect.AnnotatedMember;
+import org.codehaus.jackson.map.introspect.AnnotatedMethod;
+import org.codehaus.jackson.map.introspect.VisibilityChecker;
+
+/** a visibility checker which disables getters, but allows private access,
+ * unless {@link BidiSerialization#isStrictSerialization()} is enabled in 
which case public fields or annotations must be used.
+ * <p>
+ * the reason for this change to visibility
+ * is that getters might generate a copy, resulting in infinite loops, whereas 
field access should never do so.
+ * (see e.g. test in {@link BrooklynJacksonSerializerTest} which uses a 
sensor+config object whose getTypeToken
+ * causes infinite recursion)
+ **/
+public class PossiblyStrictPreferringFieldsVisibilityChecker implements 
VisibilityChecker<PossiblyStrictPreferringFieldsVisibilityChecker> {
+    VisibilityChecker<?> 
+        vizDefault = new VisibilityChecker.Std(Visibility.NONE, 
Visibility.NONE, Visibility.NONE, Visibility.ANY, Visibility.ANY),
+        vizStrict = new VisibilityChecker.Std(Visibility.NONE, 
Visibility.NONE, Visibility.NONE, Visibility.PUBLIC_ONLY, 
Visibility.PUBLIC_ONLY);
+    
+    @Override public PossiblyStrictPreferringFieldsVisibilityChecker 
with(JsonAutoDetect ann) { throw new UnsupportedOperationException(); }
+    @Override public PossiblyStrictPreferringFieldsVisibilityChecker 
with(Visibility v) { throw new UnsupportedOperationException(); }
+    @Override public PossiblyStrictPreferringFieldsVisibilityChecker 
withVisibility(JsonMethod method, Visibility v) { throw new 
UnsupportedOperationException(); }
+    @Override public PossiblyStrictPreferringFieldsVisibilityChecker 
withGetterVisibility(Visibility v) { throw new UnsupportedOperationException(); 
}
+    @Override public PossiblyStrictPreferringFieldsVisibilityChecker 
withIsGetterVisibility(Visibility v) { throw new 
UnsupportedOperationException(); }
+    @Override public PossiblyStrictPreferringFieldsVisibilityChecker 
withSetterVisibility(Visibility v) { throw new UnsupportedOperationException(); 
}
+    @Override public PossiblyStrictPreferringFieldsVisibilityChecker 
withCreatorVisibility(Visibility v) { throw new 
UnsupportedOperationException(); }
+    @Override public PossiblyStrictPreferringFieldsVisibilityChecker 
withFieldVisibility(Visibility v) { throw new UnsupportedOperationException(); }
+    
+    protected VisibilityChecker<?> viz() {
+        return BidiSerialization.isStrictSerialization() ? vizStrict : 
vizDefault;
+    }
+    
+    @Override public boolean isGetterVisible(Method m) { 
+        return viz().isGetterVisible(m);
+    }
+
+    @Override
+    public boolean isGetterVisible(AnnotatedMethod m) {
+        return isGetterVisible(m.getAnnotated());
+    }
+
+    @Override
+    public boolean isIsGetterVisible(Method m) {
+        return viz().isIsGetterVisible(m);
+    }
+
+    @Override
+    public boolean isIsGetterVisible(AnnotatedMethod m) {
+        return isIsGetterVisible(m.getAnnotated());
+    }
+
+    @Override
+    public boolean isSetterVisible(Method m) {
+        return viz().isSetterVisible(m);
+    }
+
+    @Override
+    public boolean isSetterVisible(AnnotatedMethod m) {
+        return isSetterVisible(m.getAnnotated());
+    }
+
+    @Override
+    public boolean isCreatorVisible(Member m) {
+        return viz().isCreatorVisible(m);
+    }
+
+    @Override
+    public boolean isCreatorVisible(AnnotatedMember m) {
+        return isCreatorVisible(m.getMember());
+    }
+
+    @Override
+    public boolean isFieldVisible(Field f) {
+        return viz().isFieldVisible(f);
+    }
+
+    @Override
+    public boolean isFieldVisible(AnnotatedField f) {
+        return isFieldVisible(f.getAnnotated());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/main/webapp/WEB-INF/web.xml
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/webapp/WEB-INF/web.xml 
b/usage/rest-server/src/main/webapp/WEB-INF/web.xml
index 9cbd5d7..2e0aeda 100644
--- a/usage/rest-server/src/main/webapp/WEB-INF/web.xml
+++ b/usage/rest-server/src/main/webapp/WEB-INF/web.xml
@@ -24,7 +24,7 @@
 
     <filter>
         <filter-name>Brooklyn Request Tagging Filter</filter-name>
-        <filter-class>brooklyn.rest.filter.RequestTaggingFilter</filter-class>
+        
<filter-class>org.apache.brooklyn.rest.filter.RequestTaggingFilter</filter-class>
     </filter>
     <filter-mapping>
         <filter-name>Brooklyn Request Tagging Filter</filter-name>
@@ -33,7 +33,7 @@
 
     <filter>
         <filter-name>Brooklyn Properties Authentication Filter</filter-name>
-        
<filter-class>brooklyn.rest.filter.BrooklynPropertiesSecurityFilter</filter-class>
+        
<filter-class>org.apache.brooklyn.rest.filter.BrooklynPropertiesSecurityFilter</filter-class>
     </filter>
     <filter-mapping>
         <filter-name>Brooklyn Properties Authentication Filter</filter-name>
@@ -42,7 +42,7 @@
 
     <filter>
         <filter-name>Brooklyn Logging Filter</filter-name>
-        <filter-class>brooklyn.rest.filter.LoggingFilter</filter-class>
+        
<filter-class>org.apache.brooklyn.rest.filter.LoggingFilter</filter-class>
     </filter>
     <filter-mapping>
         <filter-name>Brooklyn Logging Filter</filter-name>
@@ -51,7 +51,7 @@
 
     <filter>
         <filter-name>Brooklyn HA Master Filter</filter-name>
-        <filter-class>brooklyn.rest.filter.HaMasterCheckFilter</filter-class>
+        
<filter-class>org.apache.brooklyn.rest.filter.HaMasterCheckFilter</filter-class>
     </filter>
     <filter-mapping>
         <filter-name>Brooklyn HA Master Filter</filter-name>
@@ -81,22 +81,22 @@
             <param-name>com.sun.jersey.config.property.classnames</param-name>
             <param-value>
                 brooklyn.rest.apidoc.ApidocHelpMessageBodyWriter;
-                brooklyn.rest.util.FormMapProvider;
+                org.apache.brooklyn.rest.util.FormMapProvider;
                 org.codehaus.jackson.jaxrs.JacksonJsonProvider;
-                brooklyn.rest.resources.ActivityResource;
-                brooklyn.rest.resources.ApidocResource;
-                brooklyn.rest.resources.ApplicationResource;
-                brooklyn.rest.resources.CatalogResource;
-                brooklyn.rest.resources.EffectorResource;
-                brooklyn.rest.resources.EntityConfigResource;
-                brooklyn.rest.resources.EntityResource;
-                brooklyn.rest.resources.LocationResource;
-                brooklyn.rest.resources.PolicyConfigResource;
-                brooklyn.rest.resources.PolicyResource;
-                brooklyn.rest.resources.ScriptResource;
-                brooklyn.rest.resources.SensorResource;
-                brooklyn.rest.resources.UsageResource;
-                brooklyn.rest.resources.VersionResource;
+                org.apache.brooklyn.rest.resources.ActivityResource;
+                org.apache.brooklyn.rest.resources.ApidocResource;
+                org.apache.brooklyn.rest.resources.ApplicationResource;
+                org.apache.brooklyn.rest.resources.CatalogResource;
+                org.apache.brooklyn.rest.resources.EffectorResource;
+                org.apache.brooklyn.rest.resources.EntityConfigResource;
+                org.apache.brooklyn.rest.resources.EntityResource;
+                org.apache.brooklyn.rest.resources.LocationResource;
+                org.apache.brooklyn.rest.resources.PolicyConfigResource;
+                org.apache.brooklyn.rest.resources.PolicyResource;
+                org.apache.brooklyn.rest.resources.ScriptResource;
+                org.apache.brooklyn.rest.resources.SensorResource;
+                org.apache.brooklyn.rest.resources.UsageResource;
+                org.apache.brooklyn.rest.resources.VersionResource;
             </param-value>
         </init-param>
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/brooklyn/config/render/TestRendererHints.java
----------------------------------------------------------------------
diff --git 
a/usage/rest-server/src/test/java/brooklyn/config/render/TestRendererHints.java 
b/usage/rest-server/src/test/java/brooklyn/config/render/TestRendererHints.java
index 13860d7..131866c 100644
--- 
a/usage/rest-server/src/test/java/brooklyn/config/render/TestRendererHints.java
+++ 
b/usage/rest-server/src/test/java/brooklyn/config/render/TestRendererHints.java
@@ -18,6 +18,8 @@
  */
 package brooklyn.config.render;
 
+import brooklyn.config.render.RendererHints;
+
 /** Methods used when testing the {@link RendererHints} regiostry. */
 public class TestRendererHints {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/brooklyn/entity/brooklynnode/DeployBlueprintTest.java
----------------------------------------------------------------------
diff --git 
a/usage/rest-server/src/test/java/brooklyn/entity/brooklynnode/DeployBlueprintTest.java
 
b/usage/rest-server/src/test/java/brooklyn/entity/brooklynnode/DeployBlueprintTest.java
deleted file mode 100644
index 42f0785..0000000
--- 
a/usage/rest-server/src/test/java/brooklyn/entity/brooklynnode/DeployBlueprintTest.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * 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 brooklyn.entity.brooklynnode;
-
-import static org.testng.Assert.assertEquals;
-
-import java.net.URI;
-import java.util.List;
-import java.util.Map;
-
-import org.eclipse.jetty.server.Server;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import brooklyn.entity.basic.BasicApplication;
-import brooklyn.entity.basic.EntityLocal;
-import brooklyn.entity.brooklynnode.BrooklynNode.DeployBlueprintEffector;
-import brooklyn.entity.proxying.EntitySpec;
-import brooklyn.event.feed.http.JsonFunctions;
-import brooklyn.management.EntityManager;
-import brooklyn.rest.BrooklynRestApiLauncherTestFixture;
-import brooklyn.test.HttpTestUtils;
-import brooklyn.util.guava.Functionals;
-
-import com.google.common.base.Function;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-
-public class DeployBlueprintTest extends BrooklynRestApiLauncherTestFixture {
-
-    private static final Logger log = 
LoggerFactory.getLogger(DeployBlueprintTest.class);
-
-    Server server;
-
-    @BeforeMethod(alwaysRun=true)
-    public void setUp() throws Exception {
-        server = newServer();
-        useServerForTest(server);
-    }
-
-    @Test
-    public void testStartsAppViaEffector() throws Exception {
-        URI webConsoleUri = URI.create(getBaseUri());
-
-        EntitySpec<BrooklynNode> spec = EntitySpec.create(BrooklynNode.class);
-        EntityManager mgr = 
getManagementContextFromJettyServerAttributes(server).getEntityManager();
-        BrooklynNode node = mgr.createEntity(spec);
-        ((EntityLocal)node).setAttribute(BrooklynNode.WEB_CONSOLE_URI, 
webConsoleUri);
-        mgr.manage(node);
-        Map<String, String> params = 
ImmutableMap.of(DeployBlueprintEffector.BLUEPRINT_CAMP_PLAN.getName(), "{ 
services: [ serviceType: \"java:"+BasicApplication.class.getName()+"\" ] }");
-        String id = node.invoke(BrooklynNode.DEPLOY_BLUEPRINT, 
params).getUnchecked();
-
-        log.info("got: "+id);
-
-        String apps = 
HttpTestUtils.getContent(webConsoleUri.toString()+"/v1/applications");
-        List<String> appType = parseJsonList(apps, ImmutableList.of("spec", 
"type"), String.class);
-        assertEquals(appType, 
ImmutableList.of(BasicApplication.class.getName()));
-        
-        String status = 
HttpTestUtils.getContent(webConsoleUri.toString()+"/v1/applications/"+id+"/entities/"+id+"/sensors/service.status");
-        log.info("STATUS: "+status);
-    }
-    
-    private <T> List<T> parseJsonList(String json, List<String> elements, 
Class<T> clazz) {
-        Function<String, List<T>> func = Functionals.chain(
-                JsonFunctions.asJson(),
-                JsonFunctions.forEach(Functionals.chain(
-                        JsonFunctions.walk(elements),
-                        JsonFunctions.cast(clazz))));
-        return func.apply(json);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/brooklyn/rest/BrooklynPropertiesSecurityFilterTest.java
----------------------------------------------------------------------
diff --git 
a/usage/rest-server/src/test/java/brooklyn/rest/BrooklynPropertiesSecurityFilterTest.java
 
b/usage/rest-server/src/test/java/brooklyn/rest/BrooklynPropertiesSecurityFilterTest.java
deleted file mode 100644
index f9fa288..0000000
--- 
a/usage/rest-server/src/test/java/brooklyn/rest/BrooklynPropertiesSecurityFilterTest.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * 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 brooklyn.rest;
-
-import static org.testng.Assert.assertTrue;
-
-import java.net.URI;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.http.HttpHeaders;
-import org.apache.http.NameValuePair;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.utils.URLEncodedUtils;
-import org.apache.http.entity.ContentType;
-import org.apache.http.message.BasicNameValuePair;
-import org.eclipse.jetty.server.Server;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testng.annotations.Test;
-
-import brooklyn.rest.security.provider.AnyoneSecurityProvider;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.http.HttpTool;
-import brooklyn.util.http.HttpToolResponse;
-import brooklyn.util.time.Time;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.common.base.Charsets;
-import com.google.common.base.Stopwatch;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Lists;
-
-public class BrooklynPropertiesSecurityFilterTest extends 
BrooklynRestApiLauncherTestFixture {
-
-    private static final Logger LOG = 
LoggerFactory.getLogger(BrooklynPropertiesSecurityFilterTest.class);
-
-    // Would be great for this to be a unit test but it takes almost ten 
seconds.
-    @Test(groups = "Integration")
-    public void testInteractionOfSecurityFilterAndFormMapProvider() throws 
Exception {
-        Stopwatch stopwatch = Stopwatch.createStarted();
-        try {
-            Server server = useServerForTest(BrooklynRestApiLauncher.launcher()
-                    .securityProvider(AnyoneSecurityProvider.class)
-                    .forceUseOfDefaultCatalogWithJavaClassPath(true)
-                    .withoutJsgui()
-                    .start());
-            String appId = startAppAtNode(server);
-            String entityId = getTestEntityInApp(server, appId);
-            HttpClient client = HttpTool.httpClientBuilder()
-                    .uri(getBaseUri(server))
-                    .build();
-            List<? extends NameValuePair> nvps = Lists.newArrayList(
-                    new BasicNameValuePair("arg", "bar"));
-            String effector = 
String.format("/v1/applications/%s/entities/%s/effectors/identityEffector", 
appId, entityId);
-            HttpToolResponse response = HttpTool.httpPost(client, 
URI.create(getBaseUri() + effector),
-                    ImmutableMap.of(HttpHeaders.CONTENT_TYPE, 
ContentType.APPLICATION_FORM_URLENCODED.getMimeType()),
-                    URLEncodedUtils.format(nvps, Charsets.UTF_8).getBytes());
-
-            LOG.info("Effector response: {}", response.getContentAsString());
-            
assertTrue(HttpTool.isStatusCodeHealthy(response.getResponseCode()), "response 
code=" + response.getResponseCode());
-        } finally {
-            LOG.info("testInteractionOfSecurityFilterAndFormMapProvider 
complete in " + Time.makeTimeStringRounded(stopwatch));
-        }
-    }
-
-    private String startAppAtNode(Server server) throws Exception {
-        String blueprint = "name: TestApp\n" +
-                "location: localhost\n" +
-                "services:\n" +
-                "- type: brooklyn.test.entity.TestEntity";
-        HttpClient client = HttpTool.httpClientBuilder()
-                .uri(getBaseUri(server))
-                .build();
-        HttpToolResponse response = HttpTool.httpPost(client, 
URI.create(getBaseUri() + "/v1/applications"),
-                ImmutableMap.of(HttpHeaders.CONTENT_TYPE, 
"application/x-yaml"),
-                blueprint.getBytes());
-        assertTrue(HttpTool.isStatusCodeHealthy(response.getResponseCode()), 
"error creating app. response code=" + response.getResponseCode());
-        @SuppressWarnings("unchecked")
-        Map<String, Object> body = new 
ObjectMapper().readValue(response.getContent(), HashMap.class);
-        return (String) body.get("entityId");
-    }
-
-    @SuppressWarnings("rawtypes")
-    private String getTestEntityInApp(Server server, String appId) throws 
Exception {
-        HttpClient client = HttpTool.httpClientBuilder()
-                .uri(getBaseUri(server))
-                .build();
-        List entities = new ObjectMapper().readValue(
-                HttpTool.httpGet(client, URI.create(getBaseUri() + 
"/v1/applications/" + appId + "/entities"), MutableMap.<String, 
String>of()).getContent(), List.class);
-        LOG.info((String) ((Map) entities.get(0)).get("id"));
-        return (String) ((Map) entities.get(0)).get("id");
-    }
-}

Reply via email to