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 2016/02/18 16:47:43 UTC

[25/34] brooklyn-server git commit: REST API optional Jersey compatibility

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/HaMasterCheckFilterTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/HaMasterCheckFilterTest.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/HaMasterCheckFilterTest.java
new file mode 100644
index 0000000..424c0c1
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/HaMasterCheckFilterTest.java
@@ -0,0 +1,218 @@
+/*
+ * 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;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.File;
+import java.net.URI;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeoutException;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.mgmt.EntityManager;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.api.mgmt.ha.HighAvailabilityMode;
+import org.apache.brooklyn.api.mgmt.ha.ManagementNodeState;
+import org.apache.brooklyn.camp.brooklyn.BrooklynCampPlatformLauncherNoServer;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.mgmt.rebind.RebindTestUtils;
+import org.apache.brooklyn.entity.stock.BasicApplication;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.HttpClient;
+import org.eclipse.jetty.server.Server;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.Test;
+import org.apache.brooklyn.rest.security.provider.AnyoneSecurityProvider;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.http.HttpTool;
+import org.apache.brooklyn.util.http.HttpToolResponse;
+import org.apache.brooklyn.util.os.Os;
+import org.apache.brooklyn.util.time.Duration;
+
+import com.google.common.base.Predicates;
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableMap;
+
+public class HaMasterCheckFilterTest extends BrooklynRestApiLauncherTestFixture {
+    private static final Duration TIMEOUT = Duration.THIRTY_SECONDS;
+
+    private File mementoDir;
+    private ManagementContext writeMgmt;
+    private ManagementContext readMgmt;
+    private String appId;
+    private Server server;
+    private HttpClient client;
+
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+System.err.println("TEAR DOWN");
+        server.stop();
+        Entities.destroyAll(writeMgmt);
+        Entities.destroyAll(readMgmt);
+        Os.deleteRecursively(mementoDir);
+    }
+
+    @Test(groups = "Integration")
+    public void testEntitiesExistOnDisabledHA() throws Exception {
+        initHaCluster(HighAvailabilityMode.DISABLED, HighAvailabilityMode.DISABLED);
+        assertReadIsMaster();
+        assertEntityExists(new ReturnCodeCheck());
+    }
+
+    @Test(groups = "Integration")
+    public void testEntitiesExistOnMasterPromotion() throws Exception {
+        initHaCluster(HighAvailabilityMode.AUTO, HighAvailabilityMode.AUTO);
+        stopWriteNode();
+        assertEntityExists(new ReturnCodeCheck());
+        assertReadIsMaster();
+    }
+
+    @Test(groups = "Integration")
+    public void testEntitiesExistOnHotStandbyAndPromotion() throws Exception {
+        initHaCluster(HighAvailabilityMode.AUTO, HighAvailabilityMode.HOT_STANDBY);
+        assertEntityExists(new ReturnCodeCheck());
+        stopWriteNode();
+        assertEntityExists(new ReturnCodeAndNodeState());
+        assertReadIsMaster();
+    }
+
+    @Test(groups = "Integration")
+    public void testEntitiesExistOnHotBackup() throws Exception {
+        initHaCluster(HighAvailabilityMode.AUTO, HighAvailabilityMode.HOT_BACKUP);
+        Asserts.continually(
+                ImmutableMap.<String,Object>of(
+                        "timeout", Duration.THIRTY_SECONDS,
+                        "period", Duration.ZERO),
+                new ReturnCodeSupplier(),
+                Predicates.or(Predicates.equalTo(200), Predicates.equalTo(403)));
+    }
+
+    private HttpClient getClient(Server server) {
+        HttpClient client = HttpTool.httpClientBuilder()
+                .uri(getBaseUri(server))
+                .build();
+        return client;
+    }
+
+    private int getAppResponseCode() {
+        HttpToolResponse response = HttpTool.httpGet(
+                client, URI.create(getBaseUri(server) + "/v1/applications/" + appId),
+                ImmutableMap.<String,String>of());
+        return response.getResponseCode();
+    }
+
+    private String createApp(ManagementContext mgmt) {
+        EntityManager entityMgr = mgmt.getEntityManager();
+        Entity app = entityMgr.createEntity(EntitySpec.create(BasicApplication.class));
+        entityMgr.manage(app);
+        return app.getId();
+    }
+
+    private ManagementContext createManagementContext(File mementoDir, HighAvailabilityMode mode) {
+        ManagementContext mgmt = RebindTestUtils.managementContextBuilder(mementoDir, getClass().getClassLoader())
+                .persistPeriodMillis(1)
+                .forLive(false)
+                .emptyCatalog(true)
+                .buildUnstarted();
+
+        if (mode == HighAvailabilityMode.DISABLED) {
+            mgmt.getHighAvailabilityManager().disabled();
+        } else {
+            mgmt.getHighAvailabilityManager().start(mode);
+        }
+
+        new BrooklynCampPlatformLauncherNoServer()
+            .useManagementContext(mgmt)
+            .launch();
+
+        return mgmt;
+    }
+
+    private void initHaCluster(HighAvailabilityMode writeMode, HighAvailabilityMode readMode) throws InterruptedException, TimeoutException {
+        mementoDir = Os.newTempDir(getClass());
+
+        writeMgmt = createManagementContext(mementoDir, writeMode);
+        appId = createApp(writeMgmt);
+        writeMgmt.getRebindManager().waitForPendingComplete(TIMEOUT, true);
+
+        if (readMode == HighAvailabilityMode.DISABLED) {
+            //no HA, one node only
+            readMgmt = writeMgmt;
+        } else {
+            readMgmt = createManagementContext(mementoDir, readMode);
+        }
+
+        server = useServerForTest(BrooklynRestApiLauncher.launcher()
+                .managementContext(readMgmt)
+                .securityProvider(AnyoneSecurityProvider.class)
+                .forceUseOfDefaultCatalogWithJavaClassPath(true)
+                .withoutJsgui()
+                .disableHighAvailability(false)
+                .start());
+        client = getClient(server);
+    }
+
+    private void assertEntityExists(Callable<Integer> c) {
+        assertEquals((int)Asserts.succeedsEventually(c), 200);
+    }
+
+    private void assertReadIsMaster() {
+        assertEquals(readMgmt.getHighAvailabilityManager().getNodeState(), ManagementNodeState.MASTER);
+    }
+
+    private void stopWriteNode() {
+        writeMgmt.getHighAvailabilityManager().stop();
+    }
+
+    private class ReturnCodeCheck implements Callable<Integer> {
+        @Override
+        public Integer call() {
+            int retCode = getAppResponseCode();
+            if (retCode == 403) {
+                throw new RuntimeException("Not ready, retry. Response - " + retCode);
+            } else {
+                return retCode;
+            }
+        }
+    }
+
+    private class ReturnCodeAndNodeState extends ReturnCodeCheck {
+        @Override
+        public Integer call() {
+            Integer ret = super.call();
+            if (ret == HttpStatus.SC_OK) {
+                ManagementNodeState state = readMgmt.getHighAvailabilityManager().getNodeState();
+                if (state != ManagementNodeState.MASTER) {
+                    throw new RuntimeException("Not master yet " + state);
+                }
+            }
+            return ret;
+        }
+    }
+
+    private class ReturnCodeSupplier implements Supplier<Integer> {
+        @Override
+        public Integer get() {
+            return getAppResponseCode();
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/AbstractDomainTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/AbstractDomainTest.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/AbstractDomainTest.java
new file mode 100644
index 0000000..dc1131d
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/AbstractDomainTest.java
@@ -0,0 +1,44 @@
+/*
+ * 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.IOException;
+
+import org.testng.annotations.Test;
+
+import static org.apache.brooklyn.rest.util.RestApiTestUtils.asJson;
+import static org.apache.brooklyn.rest.util.RestApiTestUtils.fromJson;
+import static org.apache.brooklyn.rest.util.RestApiTestUtils.jsonFixture;
+import static org.testng.Assert.assertEquals;
+
+public abstract class AbstractDomainTest {
+
+    protected abstract String getPath();
+    protected abstract Object getDomainObject();
+
+    @Test
+    public void testSerializeToJSON() throws IOException {
+        assertEquals(asJson(getDomainObject()), jsonFixture(getPath()));
+    }
+
+    @Test
+    public void testDeserializeFromJSON() throws IOException {
+        assertEquals(fromJson(jsonFixture(getPath()), getDomainObject().getClass()), getDomainObject());
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/ApiErrorTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/ApiErrorTest.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/ApiErrorTest.java
new file mode 100644
index 0000000..f73bd30
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/ApiErrorTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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 static org.apache.brooklyn.rest.util.RestApiTestUtils.asJson;
+import static org.apache.brooklyn.rest.util.RestApiTestUtils.fromJson;
+import static org.apache.brooklyn.rest.util.RestApiTestUtils.jsonFixture;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+
+import java.io.IOException;
+import java.net.URI;
+
+import org.testng.annotations.Test;
+import org.testng.util.Strings;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+public class ApiErrorTest extends AbstractDomainTest {
+
+    @Override
+    protected String getPath() {
+        return "fixtures/api-error-basic.json";
+    }
+
+    @Override
+    protected Object getDomainObject() {
+        return ApiError.builder()
+                .message("explanatory message")
+                .details("accompanying details")
+                .build();
+    }
+
+    @Test
+    public void testSerializeApiErrorFromThrowable() throws IOException {
+        Exception e = new Exception("error");
+        e.setStackTrace(Thread.currentThread().getStackTrace());
+
+        ApiError error = ApiError.builderFromThrowable(e).build();
+        ApiError deserialised = fromJson(asJson(error), ApiError.class);
+
+        assertFalse(Strings.isNullOrEmpty(deserialised.getDetails()), "Expected details to contain exception stack trace");
+        assertEquals(deserialised, error);
+    }
+
+    @Test
+    public void testSerializeApiErrorWithoutDetails() throws IOException {
+        ApiError error = ApiError.builder()
+                .message("explanatory message")
+                .build();
+        assertEquals(asJson(error), jsonFixture("fixtures/api-error-no-details.json"));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/ApplicationSpecTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/ApplicationSpecTest.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/ApplicationSpecTest.java
new file mode 100644
index 0000000..b2690d6
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/ApplicationSpecTest.java
@@ -0,0 +1,40 @@
+/*
+ * 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 com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+public class ApplicationSpecTest extends AbstractDomainTest {
+
+    @Override
+    protected String getPath() {
+        return "fixtures/application-spec.json";
+    }
+
+    @Override
+    protected Object getDomainObject() {
+        EntitySpec entitySpec = new EntitySpec("Vanilla Java App", "org.apache.brooklyn.entity.java.VanillaJavaApp",
+                ImmutableMap.of("initialSize", "1", "creationScriptUrl", "http://my.brooklyn.io/storage/foo.sql"));
+        return ApplicationSpec.builder().name("myapp")
+                .entities(ImmutableSet.of(entitySpec)).locations(ImmutableSet.of("/v1/locations/1"))
+                .build();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/ApplicationTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/ApplicationTest.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/ApplicationTest.java
new file mode 100644
index 0000000..8708fb1
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/ApplicationTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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 static org.apache.brooklyn.rest.util.RestApiTestUtils.asJson;
+import static org.apache.brooklyn.rest.util.RestApiTestUtils.fromJson;
+import static org.apache.brooklyn.rest.util.RestApiTestUtils.jsonFixture;
+import static org.testng.Assert.assertEquals;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.Map;
+
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
+import org.apache.brooklyn.core.test.entity.TestApplication;
+import org.apache.brooklyn.test.Asserts;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+public class ApplicationTest {
+
+    final EntitySpec entitySpec = new EntitySpec("Vanilla Java App", "org.apache.brooklyn.entity.java.VanillaJavaApp",
+            ImmutableMap.of(
+                    "initialSize", "1",
+                    "creationScriptUrl", "http://my.brooklyn.io/storage/foo.sql"));
+
+    final ApplicationSpec applicationSpec = ApplicationSpec.builder().name("myapp")
+            .entities(ImmutableSet.of(entitySpec))
+            .locations(ImmutableSet.of("/v1/locations/1"))
+            .build();
+
+    final Map<String, URI> links = ImmutableMap.of(
+            "self", URI.create("/v1/applications/" + applicationSpec.getName()),
+            "entities", URI.create("fixtures/entity-summary-list.json"));
+    final ApplicationSummary application = new ApplicationSummary("myapp_id", applicationSpec, Status.STARTING, links);
+
+    @SuppressWarnings("serial")
+    @Test
+    public void testSerializeToJSON() throws IOException {
+        assertEquals(asJson(application), jsonFixture("fixtures/application.json"));
+    }
+
+    @Test
+    public void testDeserializeFromJSON() throws IOException {
+        assertEquals(fromJson(jsonFixture("fixtures/application.json"),
+                ApplicationSummary.class), application);
+    }
+
+    @Test
+    public void testTransitionToRunning() {
+        ApplicationSummary running = application.transitionTo(Status.RUNNING);
+        assertEquals(running.getStatus(), Status.RUNNING);
+    }
+
+    @Test
+    public void testAppInAppTest() throws IOException {
+        ManagementContext mgmt = LocalManagementContextForTests.newInstance();
+        try {
+            TestApplication app = mgmt.getEntityManager().createEntity(org.apache.brooklyn.api.entity.EntitySpec.create(TestApplication.class));
+            app.addChild(org.apache.brooklyn.api.entity.EntitySpec.create(TestApplication.class));
+            Asserts.assertEqualsIgnoringOrder(mgmt.getApplications(), ImmutableList.of(app));
+        } finally {
+            Entities.destroyAll(mgmt);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/EffectorSummaryTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/EffectorSummaryTest.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/EffectorSummaryTest.java
new file mode 100644
index 0000000..71cb64d
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/EffectorSummaryTest.java
@@ -0,0 +1,44 @@
+/*
+ * 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.net.URI;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+public class EffectorSummaryTest extends AbstractDomainTest {
+
+    @Override
+    protected String getPath() {
+        return "fixtures/effector-summary.json";
+    }
+
+    @Override
+    protected Object getDomainObject() {
+        return new EffectorSummary(
+                "stop",
+                "void",
+                ImmutableSet.<EffectorSummary.ParameterSummary<?>>of(),
+                "Effector description",
+                ImmutableMap.of(
+                        "self", URI.create("/v1/applications/redis-app/entities/redis-ent/effectors/stop")));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/EntitySpecTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/EntitySpecTest.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/EntitySpecTest.java
new file mode 100644
index 0000000..1beb078
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/EntitySpecTest.java
@@ -0,0 +1,48 @@
+/*
+ * 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 static org.apache.brooklyn.rest.util.RestApiTestUtils.fromJson;
+import static org.apache.brooklyn.rest.util.RestApiTestUtils.jsonFixture;
+import static org.testng.Assert.assertEquals;
+
+import java.io.IOException;
+
+import org.testng.annotations.Test;
+
+public class EntitySpecTest extends AbstractDomainTest {
+
+    @Override
+    protected String getPath() {
+        return "fixtures/entity.json";
+    }
+
+    @Override
+    protected Object getDomainObject() {
+        EntitySpec entitySpec = new EntitySpec("Vanilla Java App", "org.apache.brooklyn.entity.java.VanillaJavaApp");
+        return new EntitySpec[] { entitySpec };
+    }
+
+    @Test
+    public void testDeserializeFromJSONOnlyWithType() throws IOException {
+        EntitySpec actual = fromJson(jsonFixture("fixtures/entity-only-type.json"), EntitySpec.class);
+        assertEquals(actual.getType(), "org.apache.brooklyn.entity.java.VanillaJavaApp");
+        assertEquals(actual.getConfig().size(), 0);
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/EntitySummaryTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/EntitySummaryTest.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/EntitySummaryTest.java
new file mode 100644
index 0000000..2d39cc9
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/EntitySummaryTest.java
@@ -0,0 +1,48 @@
+/*
+ * 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.net.URI;
+import java.util.Map;
+
+import com.google.common.collect.Maps;
+
+public class EntitySummaryTest extends AbstractDomainTest {
+
+    @Override
+    protected String getPath() {
+        return "fixtures/entity-summary.json";
+    }
+
+    @Override
+    protected Object getDomainObject() {
+        Map<String, URI> links = Maps.newLinkedHashMap();
+        links.put("self", URI.create("/v1/applications/tesr/entities/zQsqdXzi"));
+        links.put("catalog", URI.create("/v1/catalog/entities/org.apache.brooklyn.entity.webapp.tomcat.TomcatServer"));
+        links.put("application", URI.create("/v1/applications/tesr"));
+        links.put("children", URI.create("/v1/applications/tesr/entities/zQsqdXzi/children"));
+        links.put("effectors", URI.create("fixtures/effector-summary-list.json"));
+        links.put("sensors", URI.create("fixtures/sensor-summary-list.json"));
+        links.put("activities", URI.create("fixtures/task-summary-list.json"));
+
+        return new EntitySummary("zQsqdXzi", "MyTomcat",
+                "org.apache.brooklyn.entity.webapp.tomcat.TomcatServer", null, links);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/LocationSpecTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/LocationSpecTest.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/LocationSpecTest.java
new file mode 100644
index 0000000..0e653c5
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/LocationSpecTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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 static org.apache.brooklyn.rest.util.RestApiTestUtils.asJson;
+import static org.apache.brooklyn.rest.util.RestApiTestUtils.fromJson;
+import static org.apache.brooklyn.rest.util.RestApiTestUtils.jsonFixture;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+
+import java.io.IOException;
+
+import org.testng.annotations.Test;
+
+@Deprecated
+public class LocationSpecTest {
+
+    // TODO when removing the deprecated class this tests, change the tests here to point at LocationSummary
+    
+    final LocationSpec locationSpec = LocationSpec.localhost();
+
+    @Test
+    public void testSerializeToJSON() throws IOException {
+        assertEquals(asJson(locationSpec), jsonFixture("fixtures/location.json"));
+    }
+
+    @Test
+    public void testDeserializeFromJSON() throws IOException {
+        assertEquals(fromJson(jsonFixture("fixtures/location.json"), LocationSpec.class), locationSpec);
+    }
+
+    @Test
+    public void testDeserializeFromJSONWithNoCredential() throws IOException {
+        LocationSpec loaded = fromJson(jsonFixture("fixtures/location-without-credential.json"), LocationSpec.class);
+
+        assertEquals(loaded.getSpec(), locationSpec.getSpec());
+
+        assertEquals(loaded.getConfig().size(), 1);
+        assertEquals(loaded.getConfig().get("identity"), "bob");
+        assertNull(loaded.getConfig().get("credential"));
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/LocationSummaryTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/LocationSummaryTest.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/LocationSummaryTest.java
new file mode 100644
index 0000000..6279546
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/LocationSummaryTest.java
@@ -0,0 +1,41 @@
+/*
+ * 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.net.URI;
+import java.util.Map;
+
+import com.google.common.collect.Maps;
+
+public class LocationSummaryTest extends AbstractDomainTest {
+
+    @Override
+    protected String getPath() {
+        return "fixtures/location-summary.json";
+    }
+
+    @Override
+    protected Object getDomainObject() {
+        Map<String, URI> links = Maps.newLinkedHashMap();
+        links.put("self", URI.create("/v1/locations/123"));
+
+        return new LocationSummary("123", "localhost", "localhost", null, null, links);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/SensorSummaryTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/SensorSummaryTest.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/SensorSummaryTest.java
new file mode 100644
index 0000000..106d737
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/SensorSummaryTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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 static org.apache.brooklyn.rest.util.RestApiTestUtils.asJson;
+import static org.apache.brooklyn.rest.util.RestApiTestUtils.fromJson;
+import static org.apache.brooklyn.rest.util.RestApiTestUtils.jsonFixture;
+import static org.testng.Assert.assertEquals;
+
+import java.io.IOException;
+import java.net.URI;
+
+import javax.ws.rs.core.UriBuilder;
+
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.api.sensor.Sensor;
+import org.apache.brooklyn.core.config.render.RendererHints;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.sensor.Sensors;
+import org.apache.brooklyn.core.test.entity.TestApplication;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.rest.transform.SensorTransformer;
+
+import com.google.common.collect.ImmutableMap;
+
+public class SensorSummaryTest {
+
+    private SensorSummary sensorSummary = new SensorSummary("redis.uptime", "Integer",
+            "Description", ImmutableMap.of(
+            "self", URI.create("/v1/applications/redis-app/entities/redis-ent/sensors/redis.uptime")));
+
+    private TestApplication app;
+    private TestEntity entity;
+    private ManagementContext mgmt;
+
+    @BeforeMethod(alwaysRun = true)
+    public void setUp() throws Exception {
+        app = TestApplication.Factory.newManagedInstanceForTests();
+        mgmt = app.getManagementContext();
+        entity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
+    }
+
+    @AfterMethod(alwaysRun = true)
+    public void tearDown() throws Exception {
+        if (mgmt != null) Entities.destroyAll(mgmt);
+    }
+
+    @Test
+    public void testSerializeToJSON() throws IOException {
+        assertEquals(asJson(sensorSummary), jsonFixture("fixtures/sensor-summary.json"));
+    }
+
+    @Test
+    public void testDeserializeFromJSON() throws IOException {
+        assertEquals(fromJson(jsonFixture("fixtures/sensor-summary.json"), SensorSummary.class), sensorSummary);
+    }
+
+    @Test
+    public void testEscapesUriForSensorName() throws IOException {
+        Sensor<String> sensor = Sensors.newStringSensor("name with space");
+        SensorSummary summary = SensorTransformer.sensorSummary(entity, sensor, UriBuilder.fromPath("/v1"));
+        URI selfUri = summary.getLinks().get("self");
+
+        String expectedUri = "/v1/applications/" + entity.getApplicationId() + "/entities/" + entity.getId() + "/sensors/" + "name%20with%20space";
+
+        assertEquals(selfUri, URI.create(expectedUri));
+    }
+
+    // Previously failed because immutable-map builder threw exception if put same key multiple times,
+    // and the NamedActionWithUrl did not have equals/hashCode
+    @Test
+    public void testSensorWithMultipleOpenUrlActionsRegistered() throws IOException {
+        AttributeSensor<String> sensor = Sensors.newStringSensor("sensor1");
+        entity.sensors().set(sensor, "http://myval");
+        RendererHints.register(sensor, RendererHints.namedActionWithUrl());
+        RendererHints.register(sensor, RendererHints.namedActionWithUrl());
+
+        SensorSummary summary = SensorTransformer.sensorSummary(entity, sensor, UriBuilder.fromPath("/"));
+
+        assertEquals(summary.getLinks().get("action:open"), URI.create("http://myval"));
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/VersionSummaryTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/VersionSummaryTest.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/VersionSummaryTest.java
new file mode 100644
index 0000000..7bed51b
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/domain/VersionSummaryTest.java
@@ -0,0 +1,49 @@
+/*
+ * 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 com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class VersionSummaryTest  extends AbstractDomainTest {
+
+    @Override
+    protected String getPath() {
+        return "fixtures/server-version.json";
+    }
+
+    @Override
+    protected Object getDomainObject() {
+        BrooklynFeatureSummary features = new BrooklynFeatureSummary(
+                "Sample Brooklyn Project com.acme.sample:brooklyn-sample v0.1.0-SNAPSHOT",
+                "com.acme.sample.brooklyn-sample",
+                "0.1.0.SNAPSHOT",
+                "523305000",
+                ImmutableMap.of("Brooklyn-Feature-Build-Id", "e0fee1adf795c84eec4735f039503eb18d9c35cc")
+        );
+        return new VersionSummary(
+                "0.7.0-SNAPSHOT",
+                "cb4f0a3af2f5042222dd176edc102bfa64e7e0b5",
+                "versions",
+                ImmutableList.of(features)
+        );
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/entitlement/AbstractRestApiEntitlementsTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/entitlement/AbstractRestApiEntitlementsTest.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/entitlement/AbstractRestApiEntitlementsTest.java
new file mode 100644
index 0000000..73449ea
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/entitlement/AbstractRestApiEntitlementsTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.entitlement;
+
+import static org.apache.brooklyn.util.http.HttpTool.httpClientBuilder;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.net.URI;
+
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.internal.BrooklynProperties;
+import org.apache.brooklyn.core.mgmt.entitlement.Entitlements;
+import org.apache.brooklyn.core.mgmt.entitlement.PerUserEntitlementManager;
+import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
+import org.apache.brooklyn.core.test.entity.TestApplication;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.rest.BrooklynRestApiLauncher;
+import org.apache.brooklyn.rest.BrooklynRestApiLauncherTestFixture;
+import org.apache.brooklyn.util.http.HttpAsserts;
+import org.apache.brooklyn.util.http.HttpTool;
+import org.apache.brooklyn.util.http.HttpToolResponse;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.HttpClient;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+
+/**
+ * Sets up the REST api with some standard users, ready for testing entitlements.
+ */
+public abstract class AbstractRestApiEntitlementsTest extends BrooklynRestApiLauncherTestFixture {
+
+    protected ManagementContext mgmt;
+    protected TestApplication app;
+    protected TestEntity entity;
+
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        BrooklynProperties props = BrooklynProperties.Factory.newEmpty();
+        props.put(Entitlements.GLOBAL_ENTITLEMENT_MANAGER.getName(), PerUserEntitlementManager.class.getName());
+        props.put(PerUserEntitlementManager.PER_USER_ENTITLEMENTS_CONFIG_PREFIX+".myRoot", "root");
+        props.put(PerUserEntitlementManager.PER_USER_ENTITLEMENTS_CONFIG_PREFIX+".myReadonly", "readonly");
+        props.put(PerUserEntitlementManager.PER_USER_ENTITLEMENTS_CONFIG_PREFIX+".myMinimal", "minimal");
+        props.put(PerUserEntitlementManager.PER_USER_ENTITLEMENTS_CONFIG_PREFIX+".myCustom", StaticDelegatingEntitlementManager.class.getName());
+        
+        mgmt = LocalManagementContextForTests.builder(true).useProperties(props).build();
+        app = mgmt.getEntityManager().createEntity(EntitySpec.create(TestApplication.class)
+                .child(EntitySpec.create(TestEntity.class))
+                        .configure(TestEntity.CONF_NAME, "myname"));
+        entity = (TestEntity) Iterables.getOnlyElement(app.getChildren());
+        
+        useServerForTest(BrooklynRestApiLauncher.launcher()
+                .managementContext(mgmt)
+                .forceUseOfDefaultCatalogWithJavaClassPath(true)
+                .securityProvider(AuthenticateAnyoneSecurityProvider.class)
+                .start());
+    }
+
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (mgmt != null) Entities.destroyAll(mgmt);
+    }
+    
+    protected HttpClient newClient(String user) throws Exception {
+        return httpClientBuilder()
+                .uri(getBaseUri())
+                .credentials(new UsernamePasswordCredentials(user, "ignoredPassword"))
+                .build();
+    }
+
+    protected String httpGet(String user, String path) throws Exception {
+        HttpToolResponse response = HttpTool.httpGet(newClient(user), URI.create(getBaseUri()).resolve(path), ImmutableMap.<String, String>of());
+        assertTrue(HttpAsserts.isHealthyStatusCode(response.getResponseCode()), "code="+response.getResponseCode()+"; reason="+response.getReasonPhrase());
+        return response.getContentAsString();
+    }
+    
+    protected String assertPermitted(String user, String path) throws Exception {
+        return httpGet(user, path);
+    }
+
+    protected void assertForbidden(String user, String path) throws Exception {
+        HttpToolResponse response = HttpTool.httpGet(newClient(user), URI.create(getBaseUri()).resolve(path), ImmutableMap.<String, String>of());
+        assertEquals(response.getResponseCode(), 403, "code="+response.getResponseCode()+"; reason="+response.getReasonPhrase()+"; content="+response.getContentAsString());
+    }
+
+    protected void assert404(String user, String path) throws Exception {
+        HttpToolResponse response = HttpTool.httpGet(newClient(user), URI.create(getBaseUri()).resolve(path), ImmutableMap.<String, String>of());
+        assertEquals(response.getResponseCode(), 404, "code="+response.getResponseCode()+"; reason="+response.getReasonPhrase()+"; content="+response.getContentAsString());
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/entitlement/ActivityApiEntitlementsTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/entitlement/ActivityApiEntitlementsTest.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/entitlement/ActivityApiEntitlementsTest.java
new file mode 100644
index 0000000..bb10197
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/entitlement/ActivityApiEntitlementsTest.java
@@ -0,0 +1,123 @@
+/*
+ * 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.entitlement;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.mgmt.Task;
+import org.apache.brooklyn.api.mgmt.entitlement.EntitlementClass;
+import org.apache.brooklyn.api.mgmt.entitlement.EntitlementContext;
+import org.apache.brooklyn.api.mgmt.entitlement.EntitlementManager;
+import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
+import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
+import org.apache.brooklyn.core.mgmt.entitlement.Entitlements;
+import org.apache.brooklyn.entity.machine.MachineEntity;
+import org.apache.brooklyn.entity.software.base.AbstractSoftwareProcessStreamsTest;
+import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation;
+import org.apache.brooklyn.util.core.task.TaskPredicates;
+import org.apache.brooklyn.util.text.StringPredicates;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Maps;
+
+@Test(singleThreaded = true)
+public class ActivityApiEntitlementsTest extends AbstractRestApiEntitlementsTest {
+
+    protected MachineEntity machineEntity;
+    protected Task<?> subTask;
+    protected Map<String, String> streams;
+    
+    @BeforeMethod(alwaysRun=true)
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        
+        LocalhostMachineProvisioningLocation loc = app.newLocalhostProvisioningLocation();
+        machineEntity = app.addChild(EntitySpec.create(MachineEntity.class)
+                .configure(BrooklynConfigKeys.SKIP_ON_BOX_BASE_DIR_RESOLUTION, true)
+                .location(loc));
+        machineEntity.start(ImmutableList.<Location>of());
+        
+        machineEntity.execCommand("echo myval");
+        
+        Set<Task<?>> tasks = BrooklynTaskTags.getTasksInEntityContext(mgmt.getExecutionManager(), machineEntity);
+        String taskNameRegex = "ssh: echo myval";
+        
+        streams = Maps.newLinkedHashMap();
+        subTask = AbstractSoftwareProcessStreamsTest.findTaskOrSubTask(tasks, TaskPredicates.displayNameSatisfies(StringPredicates.matchesRegex(taskNameRegex))).get();
+        for (String streamId : ImmutableList.of("stdin", "stdout", "stderr")) {
+            streams.put(streamId, AbstractSoftwareProcessStreamsTest.getStreamOrFail(subTask, streamId));
+        }
+    }
+    
+    @Test(groups = "Integration")
+    public void testGetTask() throws Exception {
+        String path = "/v1/activities/"+subTask.getId();
+        assertPermitted("myRoot", path);
+        assertPermitted("myReadonly", path);
+        assertForbidden("myMinimal", path);
+        assertForbidden("unrecognisedUser", path);
+    }
+    
+    @Test(groups = "Integration")
+    public void testGetStream() throws Exception {
+        String pathPrefix = "/v1/activities/"+subTask.getId()+"/stream/";
+        for (Map.Entry<String, String> entry : streams.entrySet()) {
+            String streamId = entry.getKey();
+            String expectedStream = entry.getValue();
+
+            assertEquals(httpGet("myRoot", pathPrefix+streamId), expectedStream);
+            assertEquals(httpGet("myReadonly", pathPrefix+streamId), expectedStream);
+            assertForbidden("myMinimal", pathPrefix+streamId);
+            assertForbidden("unrecognisedUser", pathPrefix+streamId);
+            
+            StaticDelegatingEntitlementManager.setDelegate(new SeeSelectiveStreams(streamId));
+            assertEquals(httpGet("myCustom", pathPrefix+streamId), expectedStream);
+            
+            StaticDelegatingEntitlementManager.setDelegate(new SeeSelectiveStreams("differentStreamId"));
+            assertForbidden("myCustom", pathPrefix+streamId);
+        }
+    }
+    
+    public static class SeeSelectiveStreams implements EntitlementManager {
+        private final String regex;
+        
+        public SeeSelectiveStreams(String regex) {
+            this.regex = regex;
+        }
+        @Override 
+        @SuppressWarnings("unchecked")
+        public <T> boolean isEntitled(EntitlementContext context, EntitlementClass<T> entitlementClass, T entitlementClassArgument) {
+            String type = entitlementClass.entitlementClassIdentifier();
+            if (Entitlements.SEE_ACTIVITY_STREAMS.entitlementClassIdentifier().equals(type)) {
+                Entitlements.TaskAndItem<String> pair = (Entitlements.TaskAndItem<String>) entitlementClassArgument;
+                return pair.getItem().matches(regex);
+            } else {
+                return true;
+            }
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/entitlement/AuthenticateAnyoneSecurityProvider.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/entitlement/AuthenticateAnyoneSecurityProvider.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/entitlement/AuthenticateAnyoneSecurityProvider.java
new file mode 100644
index 0000000..b7264b2
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/entitlement/AuthenticateAnyoneSecurityProvider.java
@@ -0,0 +1,41 @@
+/*
+ * 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.entitlement;
+
+import javax.servlet.http.HttpSession;
+
+import org.apache.brooklyn.rest.security.provider.SecurityProvider;
+
+public class AuthenticateAnyoneSecurityProvider implements SecurityProvider {
+
+    @Override
+    public boolean isAuthenticated(HttpSession session) {
+        return false;
+    }
+
+    @Override
+    public boolean authenticate(HttpSession session, String user, String password) {
+        return user != null;
+    }
+
+    @Override
+    public boolean logout(HttpSession session) {
+        return false;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/entitlement/EntityConfigApiEntitlementsTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/entitlement/EntityConfigApiEntitlementsTest.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/entitlement/EntityConfigApiEntitlementsTest.java
new file mode 100644
index 0000000..cbda515
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/entitlement/EntityConfigApiEntitlementsTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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.entitlement;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.mgmt.entitlement.EntitlementClass;
+import org.apache.brooklyn.api.mgmt.entitlement.EntitlementContext;
+import org.apache.brooklyn.api.mgmt.entitlement.EntitlementManager;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.core.config.render.RendererHints;
+import org.apache.brooklyn.core.mgmt.entitlement.Entitlements;
+import org.apache.brooklyn.core.mgmt.entitlement.Entitlements.EntityAndItem;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.rest.api.SensorApi;
+import org.apache.brooklyn.rest.resources.SensorResource;
+import org.apache.brooklyn.test.Asserts;
+import org.testng.annotations.Test;
+
+/**
+ * Test the {@link SensorApi} implementation.
+ * <p>
+ * Check that {@link SensorResource} correctly renders {@link AttributeSensor}
+ * values, including {@link RendererHints.DisplayValue} hints.
+ */
+@Test(singleThreaded = true)
+public class EntityConfigApiEntitlementsTest extends AbstractRestApiEntitlementsTest {
+
+    @Test(groups = "Integration")
+    public void testGet() throws Exception {
+        String path = "/v1/applications/"+app.getId()+"/entities/"+entity.getId()+"/config/"+TestEntity.CONF_NAME.getName();
+        String val = "\"myname\"";
+        
+        assertEquals(httpGet("myRoot", path), val);
+        assertEquals(httpGet("myReadonly", path), val);
+        assert404("myMinimal", path); // can't see app, to retrieve entity
+        assert404("unrecognisedUser", path);
+
+        StaticDelegatingEntitlementManager.setDelegate(new SeeSelectiveConfig(entity, TestEntity.CONF_NAME.getName()));
+        assertEquals(httpGet("myCustom", path), val);
+        
+        StaticDelegatingEntitlementManager.setDelegate(new SeeSelectiveConfig(entity, "differentConfName"));
+        assertForbidden("myCustom", path);
+    }
+    
+    @Test(groups = "Integration")
+    public void testCurrentState() throws Exception {
+        String path = "/v1/applications/"+app.getId()+"/entities/"+entity.getId()+"/config/current-state";
+        String confName = TestEntity.CONF_NAME.getName();
+        String regex = ".*"+confName+".*myname.*";
+        
+        Asserts.assertStringMatchesRegex(httpGet("myRoot", path), regex);
+        Asserts.assertStringMatchesRegex(httpGet("myReadonly", path), regex);
+        assert404("myMinimal", path); // can't see app, to retrieve entity
+        assert404("unrecognisedUser", path);
+
+        StaticDelegatingEntitlementManager.setDelegate(new SeeSelectiveConfig(entity, confName));
+        Asserts.assertStringMatchesRegex(httpGet("myCustom", path), regex);
+        
+        StaticDelegatingEntitlementManager.setDelegate(new SeeSelectiveConfig(entity, "differentConfName"));
+        String resp = httpGet("myCustom", path);
+        assertFalse(resp.matches(regex), "resp="+resp);
+    }
+    
+    public static class SeeSelectiveConfig implements EntitlementManager {
+        private final Entity entity;
+        private final String regex;
+        
+        public SeeSelectiveConfig(Entity entity, String regex) {
+            this.entity = entity;
+            this.regex = regex;
+        }
+        @Override 
+        @SuppressWarnings("unchecked")
+        public <T> boolean isEntitled(EntitlementContext context, EntitlementClass<T> entitlementClass, T entitlementClassArgument) {
+            String type = entitlementClass.entitlementClassIdentifier();
+            if (Entitlements.SEE_CONFIG.entitlementClassIdentifier().equals(type)) {
+                EntityAndItem<String> entityAndItem = (EntityAndItem<String>) entitlementClassArgument;
+                return entity.equals(entityAndItem.getEntity()) && entityAndItem.getItem().matches(regex);
+            } else {
+                return true;
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/entitlement/SensorApiEntitlementsTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/entitlement/SensorApiEntitlementsTest.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/entitlement/SensorApiEntitlementsTest.java
new file mode 100644
index 0000000..dab72ec
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/entitlement/SensorApiEntitlementsTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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.entitlement;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.mgmt.entitlement.EntitlementClass;
+import org.apache.brooklyn.api.mgmt.entitlement.EntitlementContext;
+import org.apache.brooklyn.api.mgmt.entitlement.EntitlementManager;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.core.config.render.RendererHints;
+import org.apache.brooklyn.core.mgmt.entitlement.Entitlements;
+import org.apache.brooklyn.core.mgmt.entitlement.Entitlements.EntityAndItem;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.rest.api.SensorApi;
+import org.apache.brooklyn.rest.resources.SensorResource;
+import org.apache.brooklyn.test.Asserts;
+import org.testng.annotations.Test;
+
+/**
+ * Test the {@link SensorApi} implementation.
+ * <p>
+ * Check that {@link SensorResource} correctly renders {@link AttributeSensor}
+ * values, including {@link RendererHints.DisplayValue} hints.
+ */
+@Test(singleThreaded = true)
+public class SensorApiEntitlementsTest extends AbstractRestApiEntitlementsTest {
+
+    @Test(groups = "Integration")
+    public void testGet() throws Exception {
+        entity.sensors().set(TestEntity.NAME, "myval");
+        
+        String sensorName = TestEntity.NAME.getName();
+        String path = "/v1/applications/"+app.getId()+"/entities/"+entity.getId()+"/sensors/"+sensorName;
+        String val = "\"myval\"";
+        
+        assertEquals(httpGet("myRoot", path), val);
+        assertEquals(httpGet("myReadonly", path), val);
+        assert404("myMinimal", path); // can't see app, to retrieve entity
+        assert404("unrecognisedUser", path);
+
+        StaticDelegatingEntitlementManager.setDelegate(new SeeSelectiveSensor(entity, sensorName));
+        assertEquals(httpGet("myCustom", path), val);
+        
+        StaticDelegatingEntitlementManager.setDelegate(new SeeSelectiveSensor(entity, "differentConfName"));
+        assertForbidden("myCustom", path);
+    }
+    
+    @Test(groups = "Integration")
+    public void testCurrentState() throws Exception {
+        entity.sensors().set(TestEntity.NAME, "myval");
+        
+        String path = "/v1/applications/"+app.getId()+"/entities/"+entity.getId()+"/sensors/current-state";
+        String sensorName = TestEntity.NAME.getName();
+        String regex = ".*"+sensorName+".*myval.*";
+        
+        Asserts.assertStringMatchesRegex(httpGet("myRoot", path), regex);
+        Asserts.assertStringMatchesRegex(httpGet("myReadonly", path), regex);
+        assert404("myMinimal", path); // can't see app, to retrieve entity
+        assert404("unrecognisedUser", path);
+
+        StaticDelegatingEntitlementManager.setDelegate(new SeeSelectiveSensor(entity, sensorName));
+        Asserts.assertStringMatchesRegex(httpGet("myCustom", path), regex);
+        
+        StaticDelegatingEntitlementManager.setDelegate(new SeeSelectiveSensor(entity, "differentSensorName"));
+        String resp = httpGet("myCustom", path);
+        assertFalse(resp.matches(regex), "resp="+resp);
+    }
+    
+    public static class SeeSelectiveSensor implements EntitlementManager {
+        private final Entity entity;
+        private final String regex;
+        
+        public SeeSelectiveSensor(Entity entity, String regex) {
+            this.entity = entity;
+            this.regex = regex;
+        }
+        @Override 
+        @SuppressWarnings("unchecked")
+        public <T> boolean isEntitled(EntitlementContext context, EntitlementClass<T> entitlementClass, T entitlementClassArgument) {
+            String type = entitlementClass.entitlementClassIdentifier();
+            if (Entitlements.SEE_SENSOR.entitlementClassIdentifier().equals(type)) {
+                EntityAndItem<String> entityAndItem = (EntityAndItem<String>) entitlementClassArgument;
+                return entity.equals(entityAndItem.getEntity()) && entityAndItem.getItem().matches(regex);
+            } else {
+                return true;
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/entitlement/ServerApiEntitlementsTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/entitlement/ServerApiEntitlementsTest.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/entitlement/ServerApiEntitlementsTest.java
new file mode 100644
index 0000000..afa42cb
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/entitlement/ServerApiEntitlementsTest.java
@@ -0,0 +1,34 @@
+/*
+ * 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.entitlement;
+
+import org.testng.annotations.Test;
+
+@Test(singleThreaded = true)
+public class ServerApiEntitlementsTest extends AbstractRestApiEntitlementsTest {
+
+    @Test(groups = "Integration")
+    public void testGetHealthy() throws Exception {
+        String path = "/v1/server/up";
+        assertPermitted("myRoot", path);
+        assertForbidden("myReadonly", path);
+        assertForbidden("myMinimal", path);
+        assertForbidden("unrecognisedUser", path);
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/entitlement/StaticDelegatingEntitlementManager.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/entitlement/StaticDelegatingEntitlementManager.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/entitlement/StaticDelegatingEntitlementManager.java
new file mode 100644
index 0000000..4370ad6
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/entitlement/StaticDelegatingEntitlementManager.java
@@ -0,0 +1,37 @@
+/*
+ * 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.entitlement;
+
+import org.apache.brooklyn.api.mgmt.entitlement.EntitlementClass;
+import org.apache.brooklyn.api.mgmt.entitlement.EntitlementContext;
+import org.apache.brooklyn.api.mgmt.entitlement.EntitlementManager;
+
+public class StaticDelegatingEntitlementManager implements EntitlementManager {
+
+    private static volatile EntitlementManager delegate;
+    
+    public static void setDelegate(EntitlementManager val) {
+        delegate = val;
+    }
+    
+    @Override
+    public <T> boolean isEntitled(EntitlementContext context, EntitlementClass<T> entitlementClass, T entitlementClassArgument) {
+        return (delegate != null) ? delegate.isEntitled(context, entitlementClass, entitlementClassArgument) : false;
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/resources/AccessResourceTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/resources/AccessResourceTest.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/resources/AccessResourceTest.java
new file mode 100644
index 0000000..0839ea5
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/resources/AccessResourceTest.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.rest.resources;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import javax.ws.rs.core.Response;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+
+import org.apache.brooklyn.rest.domain.AccessSummary;
+import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest;
+
+import com.sun.jersey.api.client.ClientResponse;
+
+@Test(singleThreaded = true)
+public class AccessResourceTest extends BrooklynRestResourceTest {
+
+    @SuppressWarnings("unused")
+    private static final Logger log = LoggerFactory.getLogger(AccessResourceTest.class);
+
+    @Test
+    public void testGetAndSetAccessControl() throws Exception {
+        // Default is everything allowed
+        AccessSummary summary = client().resource("/v1/access").get(AccessSummary.class);
+        assertTrue(summary.isLocationProvisioningAllowed());
+
+        // Forbid location provisioning
+        ClientResponse response = client().resource(
+                "/v1/access/locationProvisioningAllowed")
+                .queryParam("allowed", "false")
+                .post(ClientResponse.class);
+        assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
+
+        AccessSummary summary2 = client().resource("/v1/access").get(AccessSummary.class);
+        assertFalse(summary2.isLocationProvisioningAllowed());
+        
+        // Allow location provisioning
+        ClientResponse response2 = client().resource(
+                "/v1/access/locationProvisioningAllowed")
+                .queryParam("allowed", "true")
+                .post(ClientResponse.class);
+        assertEquals(response2.getStatus(), Response.Status.OK.getStatusCode());
+
+        AccessSummary summary3 = client().resource("/v1/access").get(AccessSummary.class);
+        assertTrue(summary3.isLocationProvisioningAllowed());
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/resources/ApidocResourceTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/resources/ApidocResourceTest.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/resources/ApidocResourceTest.java
new file mode 100644
index 0000000..739d63f
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/resources/ApidocResourceTest.java
@@ -0,0 +1,177 @@
+/*
+ * 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 com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import com.sun.jersey.api.core.ClassNamesResourceConfig;
+import com.sun.jersey.spi.container.servlet.ServletContainer;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import org.apache.brooklyn.rest.BrooklynRestApi;
+import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest;
+
+import com.sun.jersey.test.framework.AppDescriptor;
+import com.sun.jersey.test.framework.JerseyTest;
+import com.sun.jersey.test.framework.WebAppDescriptor;
+import com.sun.jersey.test.framework.spi.container.TestContainerException;
+import com.sun.jersey.test.framework.spi.container.TestContainerFactory;
+import com.sun.jersey.test.framework.spi.container.grizzly2.web.GrizzlyWebTestContainerFactory;
+import io.swagger.annotations.Api;
+import io.swagger.models.Info;
+import io.swagger.models.Operation;
+import io.swagger.models.Path;
+import io.swagger.models.Swagger;
+import java.util.Collection;
+import org.apache.brooklyn.rest.api.CatalogApi;
+import org.apache.brooklyn.rest.api.EffectorApi;
+import org.apache.brooklyn.rest.api.EntityApi;
+import org.apache.brooklyn.rest.filter.SwaggerFilter;
+import org.apache.brooklyn.rest.util.ShutdownHandlerProvider;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+
+/**
+ * @author Adam Lowe
+ */
+@Test(singleThreaded = true)
+public class ApidocResourceTest extends BrooklynRestResourceTest {
+
+    private static final Logger log = LoggerFactory.getLogger(ApidocResourceTest.class);
+
+    @Override
+    protected JerseyTest createJerseyTest() {
+        return new JerseyTest() {
+            @Override
+            protected AppDescriptor configure() {
+                return new WebAppDescriptor.Builder(
+                        ImmutableMap.of(
+                                ServletContainer.RESOURCE_CONFIG_CLASS, ClassNamesResourceConfig.class.getName(),
+                                ClassNamesResourceConfig.PROPERTY_CLASSNAMES, getResourceClassnames()))
+                        .addFilter(SwaggerFilter.class, "SwaggerFilter").build();
+            }
+
+            @Override
+            protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+                return new GrizzlyWebTestContainerFactory();
+            }
+
+            private String getResourceClassnames() {
+                Iterable<String> classnames = Collections2.transform(config.getClasses(), new Function<Class, String>() {
+                    @Override
+                    public String apply(Class clazz) {
+                        return clazz.getName();
+                    }
+                });
+                classnames = Iterables.concat(classnames, Collections2.transform(config.getSingletons(), new Function<Object, String>() {
+                    @Override
+                    public String apply(Object singleton) {
+                        return singleton.getClass().getName();
+                    }
+                }));
+                return Joiner.on(';').join(classnames);
+            }
+        };
+    }
+
+    @Override
+    protected void addBrooklynResources() {
+        for (Object o : BrooklynRestApi.getApidocResources()) {
+            addResource(o);
+        }
+        super.addBrooklynResources();
+    }
+    
+    @Test(enabled = false)
+    public void testRootSerializesSensibly() throws Exception {
+        String data = resource("/v1/apidoc/swagger.json").get(String.class);
+        log.info("apidoc gives: "+data);
+        // make sure no scala gets in
+        assertFalse(data.contains("$"));
+        assertFalse(data.contains("scala"));
+        // make sure it's an appropriate swagger 2.0 json
+        Swagger swagger = resource("/v1/apidoc/swagger.json").get(Swagger.class);
+        assertEquals(swagger.getSwagger(), "2.0");
+    }
+    
+    @Test(enabled = false)
+    public void testCountRestResources() throws Exception {
+        Swagger swagger = resource("/v1/apidoc/swagger.json").get(Swagger.class);
+        assertEquals(swagger.getTags().size(), 1 + Iterables.size(BrooklynRestApi.getBrooklynRestResources()));
+    }
+
+    @Test(enabled = false)
+    public void testApiDocDetails() throws Exception {
+        Swagger swagger = resource("/v1/apidoc/swagger.json").get(Swagger.class);
+        Collection<Operation> operations = getTaggedOperations(swagger, ApidocResource.class.getAnnotation(Api.class).value());
+        assertEquals(operations.size(), 2, "ops="+operations);
+    }
+
+    @Test(enabled = false)
+    public void testEffectorDetails() throws Exception {
+        Swagger swagger = resource("/v1/apidoc/swagger.json").get(Swagger.class);
+        Collection<Operation> operations = getTaggedOperations(swagger, EffectorApi.class.getAnnotation(Api.class).value());
+        assertEquals(operations.size(), 2, "ops="+operations);
+    }
+
+    @Test(enabled = false)
+    public void testEntityDetails() throws Exception {
+        Swagger swagger = resource("/v1/apidoc/swagger.json").get(Swagger.class);
+        Collection<Operation> operations = getTaggedOperations(swagger, EntityApi.class.getAnnotation(Api.class).value());
+        assertEquals(operations.size(), 14, "ops="+operations);
+    }
+
+    @Test(enabled = false)
+    public void testCatalogDetails() throws Exception {
+        Swagger swagger = resource("/v1/apidoc/swagger.json").get(Swagger.class);
+        Collection<Operation> operations = getTaggedOperations(swagger, CatalogApi.class.getAnnotation(Api.class).value());
+        assertEquals(operations.size(), 22, "ops="+operations);
+    }
+
+    /**
+     * Retrieves all operations tagged the given tag from the given swagger spec.
+     */
+    private Collection<Operation> getTaggedOperations(Swagger swagger, final String requiredTag) {
+        Iterable<Operation> allOperations = Iterables.concat(Collections2.transform(swagger.getPaths().values(),
+                new Function<Path, Collection<Operation>>() {
+                    @Override
+                    public Collection<Operation> apply(Path path) {
+                        return path.getOperations();
+                    }
+                }));
+
+        return Collections2.filter(ImmutableList.copyOf(allOperations), new Predicate<Operation>() {
+            @Override
+            public boolean apply(Operation operation) {
+                return operation.getTags().contains(requiredTag);
+            }
+        });
+    }
+}
+