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:30 UTC
[12/34] brooklyn-server git commit: [BROOKLYN-183] REST API using CXF
JAX-RS 2.0 implementation
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/EntityResourceTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/EntityResourceTest.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/EntityResourceTest.java
new file mode 100644
index 0000000..decbcef
--- /dev/null
+++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/EntityResourceTest.java
@@ -0,0 +1,188 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.rest.resources;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Nullable;
+import javax.ws.rs.core.MediaType;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.EntityInternal;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.entity.stock.BasicApplication;
+import org.apache.brooklyn.rest.domain.ApplicationSpec;
+import org.apache.brooklyn.rest.domain.EntitySpec;
+import org.apache.brooklyn.rest.domain.TaskSummary;
+import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest;
+import org.apache.brooklyn.rest.testing.mocks.RestMockSimpleEntity;
+import org.apache.brooklyn.util.collections.MutableList;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.http.HttpAsserts;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import javax.ws.rs.core.Response;
+
+@Test(singleThreaded = true,
+ // by using a different suite name we disallow interleaving other tests between the methods of this test class, which wrecks the test fixtures
+ suiteName = "EntityResourceTest")
+public class EntityResourceTest extends BrooklynRestResourceTest {
+
+ private static final Logger log = LoggerFactory.getLogger(EntityResourceTest.class);
+
+ private final ApplicationSpec simpleSpec = ApplicationSpec.builder()
+ .name("simple-app")
+ .entities(ImmutableSet.of(new EntitySpec("simple-ent", RestMockSimpleEntity.class.getName())))
+ .locations(ImmutableSet.of("localhost"))
+ .build();
+
+ private EntityInternal entity;
+
+ private static final String entityEndpoint = "/applications/simple-app/entities/simple-ent";
+
+ @BeforeClass(alwaysRun = true)
+ public void setUp() throws Exception {
+ // Deploy application
+ startServer();
+ Response deploy = clientDeploy(simpleSpec);
+ waitForApplicationToBeRunning(deploy.getLocation());
+
+ // Add tag
+ entity = (EntityInternal) Iterables.find(getManagementContext().getEntityManager().getEntities(), new Predicate<Entity>() {
+ @Override
+ public boolean apply(@Nullable Entity input) {
+ return "RestMockSimpleEntity".equals(input.getEntityType().getSimpleName());
+ }
+ });
+ }
+
+ @Test
+ public void testTagsSanity() throws Exception {
+ entity.tags().addTag("foo");
+
+ Response response = client().path(entityEndpoint + "/tags")
+ .accept(MediaType.APPLICATION_JSON_TYPE)
+ .get();
+ String data = response.readEntity(String.class);
+
+ try {
+ List<Object> tags = new ObjectMapper().readValue(data, new TypeReference<List<Object>>() {});
+ Assert.assertTrue(tags.contains("foo"));
+ Assert.assertFalse(tags.contains("bar"));
+ } catch (Exception e) {
+ Exceptions.propagateIfFatal(e);
+ throw new IllegalStateException("Error with deserialization of tags list: "+e+"\n"+data, e);
+ }
+ }
+
+ @Test
+ public void testRename() throws Exception {
+ try {
+ Response response = client().path(entityEndpoint + "/name")
+ .query("name", "New Name")
+ .post(null);
+
+ HttpAsserts.assertHealthyStatusCode(response.getStatus());
+ Assert.assertTrue(entity.getDisplayName().equals("New Name"));
+ } finally {
+ // restore it for other tests!
+ entity.setDisplayName("simple-ent");
+ }
+ }
+
+ @Test
+ public void testAddChild() throws Exception {
+ try {
+ // to test in GUI:
+ // services: [ { type: org.apache.brooklyn.entity.stock.BasicEntity }]
+ Response response = client().path(entityEndpoint + "/children").query("timeout", "10s")
+ .post(javax.ws.rs.client.Entity.entity("services: [ { type: "+TestEntity.class.getName()+" }]", "application/yaml"));
+
+ HttpAsserts.assertHealthyStatusCode(response.getStatus());
+ Assert.assertEquals(entity.getChildren().size(), 1);
+ Entity child = Iterables.getOnlyElement(entity.getChildren());
+ Assert.assertTrue(Entities.isManaged(child));
+
+ TaskSummary task = response.readEntity(TaskSummary.class);
+ Assert.assertEquals(task.getResult(), MutableList.of(child.getId()));
+
+ } finally {
+ // restore it for other tests
+ Collection<Entity> children = entity.getChildren();
+ if (!children.isEmpty()) Entities.unmanage(Iterables.getOnlyElement(children));
+ }
+ }
+
+ @Test
+ public void testTagsDoNotSerializeTooMuch() throws Exception {
+ entity.tags().addTag("foo");
+ entity.tags().addTag(entity.getParent());
+
+ Response response = client().path(entityEndpoint + "/tags")
+ .accept(MediaType.APPLICATION_JSON)
+ .get();
+ String raw = response.readEntity(String.class);
+ log.info("TAGS raw: "+raw);
+ HttpAsserts.assertHealthyStatusCode(response.getStatus());
+
+ Assert.assertTrue(raw.contains(entity.getParent().getId()), "unexpected app tag, does not include ID: "+raw);
+
+ Assert.assertTrue(raw.length() < 1000, "unexpected app tag, includes too much mgmt info (len "+raw.length()+"): "+raw);
+
+ Assert.assertFalse(raw.contains(entity.getManagementContext().getManagementNodeId()), "unexpected app tag, includes too much mgmt info: "+raw);
+ Assert.assertFalse(raw.contains("managementContext"), "unexpected app tag, includes too much mgmt info: "+raw);
+ Assert.assertFalse(raw.contains("localhost"), "unexpected app tag, includes too much mgmt info: "+raw);
+ Assert.assertFalse(raw.contains("catalog"), "unexpected app tag, includes too much mgmt info: "+raw);
+
+ @SuppressWarnings("unchecked")
+ List<Object> tags = mapper().readValue(raw, List.class);
+ log.info("TAGS are: "+tags);
+
+ Assert.assertEquals(tags.size(), 2, "tags are: "+tags);
+
+ Assert.assertTrue(tags.contains("foo"));
+ Assert.assertFalse(tags.contains("bar"));
+
+ MutableList<Object> appTags = MutableList.copyOf(tags);
+ appTags.remove("foo");
+ Object appTag = Iterables.getOnlyElement( appTags );
+
+ // it's a map at this point, because there was no way to make it something stronger than Object
+ Assert.assertTrue(appTag instanceof Map, "Should have deserialized an entity: "+appTag);
+ // let's re-serialize it as an entity
+ appTag = mapper().readValue(mapper().writeValueAsString(appTag), Entity.class);
+
+ Assert.assertTrue(appTag instanceof Entity, "Should have deserialized an entity: "+appTag);
+ Assert.assertEquals( ((Entity)appTag).getId(), entity.getApplicationId(), "Wrong ID: "+appTag);
+ Assert.assertTrue(appTag instanceof BasicApplication, "Should have deserialized BasicApplication: "+appTag);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/ErrorResponseTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/ErrorResponseTest.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/ErrorResponseTest.java
new file mode 100644
index 0000000..15f3fcf
--- /dev/null
+++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/ErrorResponseTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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.collect.ImmutableMap;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response.Status;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import org.apache.brooklyn.rest.domain.ApiError;
+import org.apache.brooklyn.rest.domain.ApplicationSpec;
+import org.apache.brooklyn.rest.domain.EntitySpec;
+import org.apache.brooklyn.rest.domain.PolicySummary;
+import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest;
+import org.apache.brooklyn.rest.testing.mocks.RestMockSimpleEntity;
+import org.apache.brooklyn.rest.testing.mocks.RestMockSimplePolicy;
+
+import com.google.common.collect.ImmutableSet;
+import javax.ws.rs.core.Response;
+
+@Test( // by using a different suite name we disallow interleaving other tests between the methods of this test class, which wrecks the test fixtures
+ suiteName = "ErrorResponseTest")
+public class ErrorResponseTest extends BrooklynRestResourceTest {
+
+ private final ApplicationSpec simpleSpec = ApplicationSpec.builder().name("simple-app").entities(
+ ImmutableSet.of(new EntitySpec("simple-ent", RestMockSimpleEntity.class.getName()))).locations(
+ ImmutableSet.of("localhost")).build();
+ private String policyId;
+
+ @BeforeClass(alwaysRun = true)
+ public void setUp() throws Exception {
+ startServer();
+ Response aResponse = clientDeploy(simpleSpec);
+ waitForApplicationToBeRunning(aResponse.getLocation());
+
+ String policiesEndpoint = "/applications/simple-app/entities/simple-ent/policies";
+
+ Response pResponse = client().path(policiesEndpoint)
+ .query("type", RestMockSimplePolicy.class.getCanonicalName())
+ .type(MediaType.APPLICATION_JSON_TYPE)
+ .post(toJsonEntity(ImmutableMap.of()));
+ PolicySummary response = pResponse.readEntity(PolicySummary.class);
+ assertNotNull(response.getId());
+ policyId = response.getId();
+ }
+
+ @Test
+ public void testResponseToBadRequest() {
+ String resource = "/applications/simple-app/entities/simple-ent/policies/"+policyId+"/config/"
+ + RestMockSimplePolicy.INTEGER_CONFIG.getName() + "/set";
+
+ Response response = client().path(resource)
+ .query("value", "notanumber")
+ .post(null);
+
+ assertEquals(response.getStatus(), Status.BAD_REQUEST.getStatusCode());
+ assertEquals(response.getHeaders().getFirst("Content-Type"), MediaType.APPLICATION_JSON);
+
+ ApiError error = response.readEntity(ApiError.class);
+ assertTrue(error.getMessage().toLowerCase().contains("cannot coerce"));
+ }
+
+ @Test
+ public void testResponseToWrongMethod() {
+ String resource = "/applications/simple-app/entities/simple-ent/policies/"+policyId+"/config/"
+ + RestMockSimplePolicy.INTEGER_CONFIG.getName() + "/set";
+
+ // Should be POST, not GET
+ Response response = client().path(resource)
+ .query("value", "4")
+ .get();
+
+ assertEquals(response.getStatus(), 405);
+ // Can we assert anything about the content type?
+ }
+}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/LocationResourceTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/LocationResourceTest.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/LocationResourceTest.java
new file mode 100644
index 0000000..9cf3892
--- /dev/null
+++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/LocationResourceTest.java
@@ -0,0 +1,190 @@
+/*
+ * 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.assertTrue;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Nullable;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.google.api.client.repackaged.com.google.common.base.Joiner;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import javax.ws.rs.core.GenericType;
+
+import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.core.location.SimulatedLocation;
+import org.apache.brooklyn.location.jclouds.JcloudsLocation;
+import org.apache.brooklyn.rest.domain.CatalogLocationSummary;
+import org.apache.brooklyn.rest.domain.LocationSummary;
+import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest;
+import org.apache.brooklyn.test.Asserts;
+
+
+@Test(singleThreaded = true,
+ // by using a different suite name we disallow interleaving other tests between the methods of this test class, which wrecks the test fixtures
+ suiteName = "LocationResourceTest")
+public class LocationResourceTest extends BrooklynRestResourceTest {
+
+ private static final Logger log = LoggerFactory.getLogger(LocationResourceTest.class);
+ private String legacyLocationName = "my-jungle-legacy";
+ private String legacyLocationVersion = "0.0.0.SNAPSHOT";
+
+ private String locationName = "my-jungle";
+ private String locationVersion = "0.1.2";
+
+ @Test
+ @Deprecated
+ public void testAddLegacyLocationDefinition() {
+ Map<String, String> expectedConfig = ImmutableMap.of(
+ "identity", "bob",
+ "credential", "CR3dential");
+ Response response = client().path("/locations")
+ .type(MediaType.APPLICATION_JSON_TYPE)
+ .post(new org.apache.brooklyn.rest.domain.LocationSpec(legacyLocationName, "aws-ec2:us-east-1", expectedConfig));
+
+ URI addedLegacyLocationUri = response.getLocation();
+ log.info("added legacy, at: " + addedLegacyLocationUri);
+ LocationSummary location = client().path(response.getLocation()).get(LocationSummary.class);
+ log.info(" contents: " + location);
+ assertEquals(location.getSpec(), "brooklyn.catalog:"+legacyLocationName+":"+legacyLocationVersion);
+ assertTrue(addedLegacyLocationUri.getPath().startsWith("/locations/"));
+
+ JcloudsLocation l = (JcloudsLocation) getManagementContext().getLocationRegistry().resolve(legacyLocationName);
+ Assert.assertEquals(l.getProvider(), "aws-ec2");
+ Assert.assertEquals(l.getRegion(), "us-east-1");
+ Assert.assertEquals(l.getIdentity(), "bob");
+ Assert.assertEquals(l.getCredential(), "CR3dential");
+ }
+
+ @SuppressWarnings("deprecation")
+ @Test
+ public void testAddNewLocationDefinition() {
+ String yaml = Joiner.on("\n").join(ImmutableList.of(
+ "brooklyn.catalog:",
+ " symbolicName: "+locationName,
+ " version: " + locationVersion,
+ "",
+ "brooklyn.locations:",
+ "- type: "+"aws-ec2:us-east-1",
+ " brooklyn.config:",
+ " identity: bob",
+ " credential: CR3dential"));
+
+
+ Response response = client().path("/catalog")
+ .post(yaml);
+
+ assertEquals(response.getStatus(), Response.Status.CREATED.getStatusCode());
+
+
+ URI addedCatalogItemUri = response.getLocation();
+ log.info("added, at: " + addedCatalogItemUri);
+
+ // Ensure location definition exists
+ CatalogLocationSummary locationItem = client().path("/catalog/locations/"+locationName + "/" + locationVersion)
+ .get(CatalogLocationSummary.class);
+ log.info(" item: " + locationItem);
+ LocationSummary locationSummary = client().path(URI.create("/locations/"+locationName+"/")).get(LocationSummary.class);
+ log.info(" summary: " + locationSummary);
+ Assert.assertEquals(locationSummary.getSpec(), "brooklyn.catalog:"+locationName+":"+locationVersion);
+
+ // Ensure location is usable - can instantiate, and has right config
+ JcloudsLocation l = (JcloudsLocation) getManagementContext().getLocationRegistry().resolve(locationName);
+ Assert.assertEquals(l.getProvider(), "aws-ec2");
+ Assert.assertEquals(l.getRegion(), "us-east-1");
+ Assert.assertEquals(l.getIdentity(), "bob");
+ Assert.assertEquals(l.getCredential(), "CR3dential");
+ }
+
+ @SuppressWarnings("deprecation")
+ @Test(dependsOnMethods = { "testAddNewLocationDefinition" })
+ public void testListAllLocationDefinitions() {
+ Set<LocationSummary> locations = client().path("/locations")
+ .get(new GenericType<Set<LocationSummary>>() {});
+ Iterable<LocationSummary> matching = Iterables.filter(locations, new Predicate<LocationSummary>() {
+ @Override
+ public boolean apply(@Nullable LocationSummary l) {
+ return locationName.equals(l.getName());
+ }
+ });
+ LocationSummary location = Iterables.getOnlyElement(matching);
+
+ URI expectedLocationUri = URI.create(getEndpointAddress() + "/locations/"+locationName).normalize();
+ Assert.assertEquals(location.getSpec(), "brooklyn.catalog:"+locationName+":"+locationVersion);
+ Assert.assertEquals(location.getLinks().get("self").toString(), expectedLocationUri.getPath());
+ }
+
+ @SuppressWarnings("deprecation")
+ @Test(dependsOnMethods = { "testListAllLocationDefinitions" })
+ public void testGetSpecificLocation() {
+ URI expectedLocationUri = URI.create("/locations/"+locationName);
+ LocationSummary location = client().path(expectedLocationUri).get(LocationSummary.class);
+ assertEquals(location.getSpec(), "brooklyn.catalog:"+locationName+":"+locationVersion);
+ }
+
+ @SuppressWarnings("deprecation")
+ @Test
+ public void testGetLocationConfig() {
+ SimulatedLocation parentLoc = (SimulatedLocation) getManagementContext().getLocationManager().createLocation(LocationSpec.create(SimulatedLocation.class)
+ .configure("myParentKey", "myParentVal"));
+ SimulatedLocation loc = (SimulatedLocation) getManagementContext().getLocationManager().createLocation(LocationSpec.create(SimulatedLocation.class)
+ .parent(parentLoc)
+ .configure("mykey", "myval")
+ .configure("password", "mypassword"));
+
+ // "full" means including-inherited, filtered to exclude secrets
+ LocationSummary summaryFull = client().path("/locations/"+loc.getId()).query("full","true").get(LocationSummary.class);
+ assertEquals(summaryFull.getConfig(), ImmutableMap.of("mykey", "myval", "myParentKey", "myParentVal"), "conf="+summaryFull.getConfig());
+
+ // Default is local-only, filtered to exclude secrets
+ URI uriDefault = URI.create("/locations/"+loc.getId());
+ LocationSummary summaryDefault = client().path(uriDefault).get(LocationSummary.class);
+ assertEquals(summaryDefault.getConfig(), ImmutableMap.of("mykey", "myval"), "conf="+summaryDefault.getConfig());
+ }
+
+ @Test(dependsOnMethods = { "testAddLegacyLocationDefinition" })
+ @Deprecated
+ public void testDeleteLocation() {
+ final int size = getLocationRegistry().getDefinedLocations().size();
+ URI expectedLocationUri = URI.create("/locations/"+legacyLocationName);
+
+ Response response = client().path(expectedLocationUri).delete();
+ assertEquals(response.getStatus(), Response.Status.NO_CONTENT.getStatusCode());
+ Asserts.succeedsEventually(new Runnable() {
+ @Override
+ public void run() {
+ assertEquals(getLocationRegistry().getDefinedLocations().size(), size - 1);
+ }
+ });
+ }
+}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/PolicyResourceTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/PolicyResourceTest.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/PolicyResourceTest.java
new file mode 100644
index 0000000..21f1210
--- /dev/null
+++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/PolicyResourceTest.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.rest.resources;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.fail;
+
+import java.util.Map;
+import java.util.Set;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import org.apache.brooklyn.rest.domain.ApplicationSpec;
+import org.apache.brooklyn.rest.domain.EntitySpec;
+import org.apache.brooklyn.rest.domain.PolicyConfigSummary;
+import org.apache.brooklyn.rest.domain.PolicySummary;
+import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest;
+import org.apache.brooklyn.rest.testing.mocks.RestMockSimpleEntity;
+import org.apache.brooklyn.rest.testing.mocks.RestMockSimplePolicy;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import javax.ws.rs.core.GenericType;
+
+@Test(singleThreaded = true,
+ // by using a different suite name we disallow interleaving other tests between the methods of this test class, which wrecks the test fixtures
+ suiteName = "PolicyResourceTest")
+public class PolicyResourceTest extends BrooklynRestResourceTest {
+
+ @SuppressWarnings("unused")
+ private static final Logger log = LoggerFactory.getLogger(PolicyResourceTest.class);
+
+ private static final String ENDPOINT = "/applications/simple-app/entities/simple-ent/policies/";
+
+ private final ApplicationSpec simpleSpec = ApplicationSpec.builder().name("simple-app").entities(
+ ImmutableSet.of(new EntitySpec("simple-ent", RestMockSimpleEntity.class.getName()))).locations(
+ ImmutableSet.of("localhost")).build();
+
+ private String policyId;
+
+ @BeforeClass(alwaysRun = true)
+ public void setUp() throws Exception {
+ startServer();
+ Response aResponse = clientDeploy(simpleSpec);
+ waitForApplicationToBeRunning(aResponse.getLocation());
+
+ Response pResponse = client().path(ENDPOINT)
+ .query("type", RestMockSimplePolicy.class.getCanonicalName())
+ .type(MediaType.APPLICATION_JSON_TYPE)
+ .post(toJsonEntity(ImmutableMap.of()));
+
+ PolicySummary response = pResponse.readEntity(PolicySummary.class);
+ assertNotNull(response.getId());
+ policyId = response.getId();
+
+ }
+
+ @Test
+ public void testListConfig() throws Exception {
+ Set<PolicyConfigSummary> config = client().path(ENDPOINT + policyId + "/config")
+ .get(new GenericType<Set<PolicyConfigSummary>>() {});
+
+ Set<String> configNames = Sets.newLinkedHashSet();
+ for (PolicyConfigSummary conf : config) {
+ configNames.add(conf.getName());
+ }
+
+ assertEquals(configNames, ImmutableSet.of(
+ RestMockSimplePolicy.SAMPLE_CONFIG.getName(),
+ RestMockSimplePolicy.INTEGER_CONFIG.getName()));
+ }
+
+ @Test
+ public void testGetNonExistantConfigReturns404() throws Exception {
+ String invalidConfigName = "doesnotexist";
+ try {
+ PolicyConfigSummary summary = client().path(ENDPOINT + policyId + "/config/" + invalidConfigName)
+ .get(PolicyConfigSummary.class);
+ fail("Should have thrown 404, but got "+summary);
+ } catch (Exception e) {
+ if (!e.toString().contains("404")) throw e;
+ }
+ }
+
+ @Test
+ public void testGetDefaultValue() throws Exception {
+ String configName = RestMockSimplePolicy.SAMPLE_CONFIG.getName();
+ String expectedVal = RestMockSimplePolicy.SAMPLE_CONFIG.getDefaultValue();
+
+ String configVal = client().path(ENDPOINT + policyId + "/config/" + configName)
+ .get(String.class);
+ assertEquals(configVal, expectedVal);
+ }
+
+ @Test(dependsOnMethods = "testGetDefaultValue")
+ public void testReconfigureConfig() throws Exception {
+ String configName = RestMockSimplePolicy.SAMPLE_CONFIG.getName();
+
+ Response response = client().path(ENDPOINT + policyId + "/config/" + configName + "/set")
+ .query("value", "newval")
+ .post(null);
+
+ assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
+ }
+
+ @Test(dependsOnMethods = "testReconfigureConfig")
+ public void testGetConfigValue() throws Exception {
+ String configName = RestMockSimplePolicy.SAMPLE_CONFIG.getName();
+ String expectedVal = "newval";
+
+ Map<String, Object> allState = client().path(ENDPOINT + policyId + "/config/current-state")
+ .get(new GenericType<Map<String, Object>>() {});
+ assertEquals(allState, ImmutableMap.of(configName, expectedVal));
+
+ String configVal = client().path(ENDPOINT + policyId + "/config/" + configName)
+ .get(String.class);
+ assertEquals(configVal, expectedVal);
+ }
+}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/ScriptResourceTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/ScriptResourceTest.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/ScriptResourceTest.java
new file mode 100644
index 0000000..20e504c
--- /dev/null
+++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/ScriptResourceTest.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.rest.resources;
+
+import java.util.Collections;
+
+import org.apache.brooklyn.api.entity.Application;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.Location;
+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.rest.domain.ScriptExecutionSummary;
+import org.apache.brooklyn.rest.testing.mocks.RestMockApp;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+@Test( // by using a different suite name we disallow interleaving other tests between the methods of this test class, which wrecks the test fixtures
+ suiteName = "ScriptResourceTest")
+public class ScriptResourceTest {
+
+ @Test
+ public void testGroovy() {
+ ManagementContext mgmt = LocalManagementContextForTests.newInstance();
+ Application app = mgmt.getEntityManager().createEntity( EntitySpec.create(Application.class, RestMockApp.class) );
+ try {
+
+ Entities.start(app, Collections.<Location>emptyList());
+
+ ScriptResource s = new ScriptResource();
+ s.setManagementContext(mgmt);
+
+ ScriptExecutionSummary result = s.groovy(null, "def apps = []; mgmt.applications.each { println 'app:'+it; apps << it.id }; apps");
+ Assert.assertEquals(Collections.singletonList(app.getId()).toString(), result.getResult());
+ Assert.assertTrue(result.getStdout().contains("app:RestMockApp"));
+
+ } finally { Entities.destroyAll(mgmt); }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/SensorResourceIntegrationTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/SensorResourceIntegrationTest.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/SensorResourceIntegrationTest.java
new file mode 100644
index 0000000..9b7214d
--- /dev/null
+++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/SensorResourceIntegrationTest.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.rest.resources;
+
+import java.net.URI;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.core.entity.EntityInternal;
+import org.apache.brooklyn.core.entity.EntityPredicates;
+import org.apache.brooklyn.entity.stock.BasicApplication;
+import org.apache.brooklyn.rest.testing.mocks.RestMockSimpleEntity;
+import org.apache.brooklyn.test.HttpTestUtils;
+import org.apache.brooklyn.util.collections.MutableList;
+import org.apache.brooklyn.util.http.HttpTool;
+import org.apache.brooklyn.util.http.HttpToolResponse;
+import org.apache.brooklyn.util.net.Urls;
+import org.apache.http.client.HttpClient;
+import org.testng.Assert;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest;
+
+public class SensorResourceIntegrationTest extends BrooklynRestResourceTest {
+
+ private ManagementContext mgmt;
+ private BasicApplication app;
+
+ @BeforeClass(alwaysRun = true)
+ protected void setUp() {
+ mgmt = getManagementContext();
+ app = mgmt.getEntityManager().createEntity(EntitySpec.create(BasicApplication.class).displayName("simple-app")
+ .child(EntitySpec.create(Entity.class, RestMockSimpleEntity.class).displayName("simple-ent")));
+ mgmt.getEntityManager().manage(app);
+ app.start(MutableList.of(mgmt.getLocationRegistry().resolve("localhost")));
+ }
+
+ // marked integration because of time
+ @Test(groups = "Integration")
+ public void testSensorBytes() throws Exception {
+ EntityInternal entity = (EntityInternal) Iterables.find(mgmt.getEntityManager().getEntities(), EntityPredicates.displayNameEqualTo("simple-ent"));
+ SensorResourceTest.addAmphibianSensor(entity);
+
+ String baseUri = getEndpointAddress();
+ URI url = URI.create(Urls.mergePaths(baseUri, SensorResourceTest.SENSORS_ENDPOINT, SensorResourceTest.SENSOR_NAME));
+
+ // Uses explicit "application/json" because failed on jenkins as though "text/plain" was the default on Ubuntu jenkins!
+ HttpClient client = HttpTool.httpClientBuilder().uri(baseUri).build();
+ HttpToolResponse response = HttpTool.httpGet(client, url, ImmutableMap.<String, String>of("Accept", "application/json"));
+ HttpTestUtils.assertHealthyStatusCode(response.getResponseCode());
+ Assert.assertEquals(response.getContentAsString(), "\"12345 frogs\"");
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/SensorResourceTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/SensorResourceTest.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/SensorResourceTest.java
new file mode 100644
index 0000000..01b3546
--- /dev/null
+++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/SensorResourceTest.java
@@ -0,0 +1,267 @@
+/*
+ * 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 java.util.Map;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.core.config.render.RendererHints;
+import org.apache.brooklyn.core.entity.EntityInternal;
+import org.apache.brooklyn.core.entity.EntityPredicates;
+import org.apache.brooklyn.core.sensor.Sensors;
+import org.apache.brooklyn.rest.api.SensorApi;
+import org.apache.brooklyn.rest.domain.ApplicationSpec;
+import org.apache.brooklyn.rest.domain.EntitySpec;
+import org.apache.brooklyn.rest.test.config.render.TestRendererHints;
+import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest;
+import org.apache.brooklyn.rest.testing.mocks.RestMockSimpleEntity;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.http.HttpAsserts;
+import org.apache.brooklyn.util.stream.Streams;
+import org.apache.brooklyn.util.text.StringFunctions;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Functions;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import java.io.InputStream;
+import javax.ws.rs.core.GenericType;
+import org.apache.cxf.jaxrs.client.WebClient;
+
+/**
+ * Test the {@link SensorApi} implementation.
+ * <p>
+ * Check that {@link SensorResource} correctly renders {@link AttributeSensor}
+ * values, including {@link RendererHints.DisplayValue} hints.
+ */
+@Test(singleThreaded = true,
+ // by using a different suite name we disallow interleaving other tests between the methods of this test class, which wrecks the test fixtures
+ suiteName = "SensorResourceTest")
+public class SensorResourceTest extends BrooklynRestResourceTest {
+
+ final static ApplicationSpec SIMPLE_SPEC = ApplicationSpec.builder()
+ .name("simple-app")
+ .entities(ImmutableSet.of(new EntitySpec("simple-ent", RestMockSimpleEntity.class.getName())))
+ .locations(ImmutableSet.of("localhost"))
+ .build();
+
+ static final String SENSORS_ENDPOINT = "/applications/simple-app/entities/simple-ent/sensors";
+ static final String SENSOR_NAME = "amphibian.count";
+ static final AttributeSensor<Integer> SENSOR = Sensors.newIntegerSensor(SENSOR_NAME);
+
+ EntityInternal entity;
+
+ /**
+ * Sets up the application and entity.
+ * <p>
+ * Adds a sensor and sets its value to {@code 12345}. Configures a display value
+ * hint that appends {@code frogs} to the value of the sensor.
+ */
+ @BeforeClass(alwaysRun = true)
+ public void setUp() throws Exception {
+ // Deploy application
+ startServer();
+ Response deploy = clientDeploy(SIMPLE_SPEC);
+ waitForApplicationToBeRunning(deploy.getLocation());
+
+ entity = (EntityInternal) Iterables.find(getManagementContext().getEntityManager().getEntities(), EntityPredicates.displayNameEqualTo("simple-ent"));
+ addAmphibianSensor(entity);
+ }
+
+ static void addAmphibianSensor(EntityInternal entity) {
+ // Add new sensor
+ entity.getMutableEntityType().addSensor(SENSOR);
+ entity.sensors().set(SENSOR, 12345);
+
+ // Register display value hint
+ RendererHints.register(SENSOR, RendererHints.displayValue(Functions.compose(StringFunctions.append(" frogs"), Functions.toStringFunction())));
+ }
+
+ @AfterClass(alwaysRun = true)
+ public void tearDown() throws Exception {
+ TestRendererHints.clearRegistry();
+ }
+
+ /** Check default is to use display value hint. */
+ @Test
+ public void testBatchSensorRead() throws Exception {
+ Response response = client().path(SENSORS_ENDPOINT + "/current-state")
+ .accept(MediaType.APPLICATION_JSON)
+ .get();
+ Map<String, ?> currentState = response.readEntity(new GenericType<Map<String,?>>(Map.class) {});
+
+ for (String sensor : currentState.keySet()) {
+ if (sensor.equals(SENSOR_NAME)) {
+ assertEquals(currentState.get(sensor), "12345 frogs");
+ }
+ }
+ }
+
+ /** Check setting {@code raw} to {@code true} ignores display value hint. */
+ @Test(dependsOnMethods = "testBatchSensorRead")
+ public void testBatchSensorReadRaw() throws Exception {
+ Response response = client().path(SENSORS_ENDPOINT + "/current-state")
+ .query("raw", "true")
+ .accept(MediaType.APPLICATION_JSON)
+ .get();
+ Map<String, ?> currentState = response.readEntity(new GenericType<Map<String,?>>(Map.class) {});
+
+ for (String sensor : currentState.keySet()) {
+ if (sensor.equals(SENSOR_NAME)) {
+ assertEquals(currentState.get(sensor), Integer.valueOf(12345));
+ }
+ }
+ }
+
+ protected Response doSensorTest(Boolean raw, MediaType acceptsType, Object expectedValue) {
+ return doSensorTestUntyped(
+ raw==null ? null : (""+raw).toLowerCase(),
+ acceptsType==null ? null : new String[] { acceptsType.toString() },
+ expectedValue);
+ }
+ protected Response doSensorTestUntyped(String raw, String[] acceptsTypes, Object expectedValue) {
+ WebClient req = client().path(SENSORS_ENDPOINT + "/" + SENSOR_NAME);
+ if (raw!=null) req = req.query("raw", raw);
+ Response response;
+ if (acceptsTypes!=null) {
+ response = req.accept(acceptsTypes).get();
+ } else {
+ response = req.get();
+ }
+ if (expectedValue!=null) {
+ HttpAsserts.assertHealthyStatusCode(response.getStatus());
+ Object value = response.readEntity(expectedValue.getClass());
+ assertEquals(value, expectedValue);
+ }
+ return response;
+ }
+
+ /**
+ * Check we can get a sensor, explicitly requesting json; gives a string picking up the rendering hint.
+ *
+ * If no "Accepts" header is given, then we don't control whether json or plain text comes back.
+ * It is dependent on the method order, which is compiler-specific.
+ */
+ @Test
+ public void testGetJson() throws Exception {
+ doSensorTest(null, MediaType.APPLICATION_JSON_TYPE, "\"12345 frogs\"");
+ }
+
+ @Test
+ public void testGetJsonBytes() throws Exception {
+ Response response = doSensorTest(null, MediaType.APPLICATION_JSON_TYPE, null);
+ byte[] bytes = Streams.readFully(response.readEntity(InputStream.class));
+ // assert we have one set of surrounding quotes
+ assertEquals(bytes.length, 13);
+ }
+
+ /** Check that plain returns a string without quotes, with the rendering hint */
+ @Test
+ public void testGetPlain() throws Exception {
+ doSensorTest(null, MediaType.TEXT_PLAIN_TYPE, "12345 frogs");
+ }
+
+ /**
+ * Check that when we set {@code raw = true}, the result ignores the display value hint.
+ *
+ * If no "Accepts" header is given, then we don't control whether json or plain text comes back.
+ * It is dependent on the method order, which is compiler-specific.
+ */
+ @Test
+ public void testGetRawJson() throws Exception {
+ doSensorTest(true, MediaType.APPLICATION_JSON_TYPE, 12345);
+ }
+
+ /** As {@link #testGetRaw()} but with plain set, returns the number */
+ @Test
+ public void testGetPlainRaw() throws Exception {
+ // have to pass a string because that's how PLAIN is deserialized
+ doSensorTest(true, MediaType.TEXT_PLAIN_TYPE, "12345");
+ }
+
+ /** Check explicitly setting {@code raw} to {@code false} is as before */
+ @Test
+ public void testGetPlainRawFalse() throws Exception {
+ doSensorTest(false, MediaType.TEXT_PLAIN_TYPE, "12345 frogs");
+ }
+
+ /** Check empty vaue for {@code raw} will revert to using default. */
+ @Test
+ public void testGetPlainRawEmpty() throws Exception {
+ doSensorTestUntyped("", new String[] { MediaType.TEXT_PLAIN }, "12345 frogs");
+ }
+
+ /** Check unparseable vaue for {@code raw} will revert to using default. */
+ @Test
+ public void testGetPlainRawError() throws Exception {
+ doSensorTestUntyped("biscuits", new String[] { MediaType.TEXT_PLAIN }, "12345 frogs");
+ }
+
+ /** Check we can set a value */
+ @Test
+ public void testSet() throws Exception {
+ try {
+ Response response = client().path(SENSORS_ENDPOINT + "/" + SENSOR_NAME)
+ .type(MediaType.APPLICATION_JSON_TYPE)
+ .post(67890);
+ assertEquals(response.getStatus(), Response.Status.NO_CONTENT.getStatusCode());
+
+ assertEquals(entity.getAttribute(SENSOR), (Integer)67890);
+
+ String value = client().path(SENSORS_ENDPOINT + "/" + SENSOR_NAME).accept(MediaType.TEXT_PLAIN_TYPE).get(String.class);
+ assertEquals(value, "67890 frogs");
+
+ } finally { addAmphibianSensor(entity); }
+ }
+
+ @Test
+ public void testSetFromMap() throws Exception {
+ try {
+ Response response = client().path(SENSORS_ENDPOINT)
+ .type(MediaType.APPLICATION_JSON_TYPE)
+ .post(MutableMap.of(SENSOR_NAME, 67890));
+ assertEquals(response.getStatus(), Response.Status.NO_CONTENT.getStatusCode());
+
+ assertEquals(entity.getAttribute(SENSOR), (Integer)67890);
+
+ } finally { addAmphibianSensor(entity); }
+ }
+
+ /** Check we can delete a value */
+ @Test
+ public void testDelete() throws Exception {
+ try {
+ Response response = client().path(SENSORS_ENDPOINT + "/" + SENSOR_NAME)
+ .delete();
+ assertEquals(response.getStatus(), Response.Status.NO_CONTENT.getStatusCode());
+
+ String value = client().path(SENSORS_ENDPOINT + "/" + SENSOR_NAME).accept(MediaType.TEXT_PLAIN_TYPE).get(String.class);
+ assertEquals(value, "");
+
+ } finally { addAmphibianSensor(entity); }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/ServerResourceTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/ServerResourceTest.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/ServerResourceTest.java
new file mode 100644
index 0000000..24f9e3d
--- /dev/null
+++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/ServerResourceTest.java
@@ -0,0 +1,168 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.rest.resources;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.brooklyn.api.entity.ImplementedBy;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.core.BrooklynVersion;
+import org.apache.brooklyn.core.internal.BrooklynProperties;
+import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
+import org.apache.brooklyn.entity.software.base.EmptySoftwareProcess;
+import org.apache.brooklyn.entity.software.base.EmptySoftwareProcessDriver;
+import org.apache.brooklyn.entity.software.base.EmptySoftwareProcessImpl;
+import org.apache.brooklyn.rest.domain.HighAvailabilitySummary;
+import org.apache.brooklyn.rest.domain.VersionSummary;
+import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+import javax.ws.rs.core.Response;
+import org.apache.cxf.jaxrs.client.WebClient;
+
+@Test(singleThreaded = true,
+ // by using a different suite name we disallow interleaving other tests between the methods of this test class, which wrecks the test fixtures
+ suiteName = "ServerResourceTest")
+public class ServerResourceTest extends BrooklynRestResourceTest {
+
+ private static final Logger log = LoggerFactory.getLogger(ServerResourceTest.class);
+
+ @Test
+ public void testGetVersion() throws Exception {
+ VersionSummary version = client().path("/server/version").get(VersionSummary.class);
+ assertEquals(version.getVersion(), BrooklynVersion.get());
+ }
+
+ @Test
+ public void testGetStatus() throws Exception {
+ String status = client().path("/server/status").get(String.class);
+ assertEquals(status, "MASTER");
+ }
+
+ @Test
+ public void testGetHighAvailability() throws Exception {
+ // Note by default management context from super is started without HA enabled.
+ // Therefore can only assert a minimal amount of stuff.
+ HighAvailabilitySummary summary = client().path("/server/highAvailability").get(HighAvailabilitySummary.class);
+ log.info("HA summary is: "+summary);
+
+ String ownNodeId = getManagementContext().getManagementNodeId();
+ assertEquals(summary.getOwnId(), ownNodeId);
+ assertEquals(summary.getMasterId(), ownNodeId);
+ assertEquals(summary.getNodes().keySet(), ImmutableSet.of(ownNodeId));
+ assertEquals(summary.getNodes().get(ownNodeId).getNodeId(), ownNodeId);
+ assertEquals(summary.getNodes().get(ownNodeId).getStatus(), "MASTER");
+ assertNotNull(summary.getNodes().get(ownNodeId).getLocalTimestamp());
+ // remote will also be non-null if there is no remote backend (local is re-used)
+ assertNotNull(summary.getNodes().get(ownNodeId).getRemoteTimestamp());
+ assertEquals(summary.getNodes().get(ownNodeId).getLocalTimestamp(), summary.getNodes().get(ownNodeId).getRemoteTimestamp());
+ }
+
+ @SuppressWarnings("serial")
+ @Test
+ public void testReloadsBrooklynProperties() throws Exception {
+ final AtomicInteger reloadCount = new AtomicInteger();
+ getManagementContext().addPropertiesReloadListener(new ManagementContext.PropertiesReloadListener() {
+ @Override public void reloaded() {
+ reloadCount.incrementAndGet();
+ }});
+ client().path("/server/properties/reload").post(null);
+ assertEquals(reloadCount.get(), 1);
+ }
+
+ @Test
+ void testGetConfig() throws Exception {
+ ((ManagementContextInternal)getManagementContext()).getBrooklynProperties().put("foo.bar.baz", "quux");
+ try {
+ assertEquals(client().path("/server/config/foo.bar.baz").get(String.class), "quux");
+ } finally {
+ ((ManagementContextInternal)getManagementContext()).getBrooklynProperties().remove("foo.bar.baz");
+ }
+ }
+
+ @Test
+ void testGetMissingConfigThrowsException() throws Exception {
+ final String key = "foo.bar.baz";
+ BrooklynProperties properties = ((ManagementContextInternal)getManagementContext()).getBrooklynProperties();
+ Object existingValue = null;
+ boolean keyAlreadyPresent = false;
+ if (properties.containsKey(key)) {
+ existingValue = properties.remove(key);
+ keyAlreadyPresent = true;
+ }
+ try {
+ final WebClient webClient = client().path("/server/config/" + key);
+ Response response = webClient.get();
+ assertEquals(response.getStatus(), 204);
+ } finally {
+ if (keyAlreadyPresent) {
+ properties.put(key, existingValue);
+ }
+ }
+ }
+
+ // Alternatively could reuse a blocking location, see org.apache.brooklyn.entity.software.base.SoftwareProcessEntityTest.ReleaseLatchLocation
+ @ImplementedBy(StopLatchEntityImpl.class)
+ public interface StopLatchEntity extends EmptySoftwareProcess {
+ public void unblock();
+ public boolean isBlocked();
+ }
+
+ public static class StopLatchEntityImpl extends EmptySoftwareProcessImpl implements StopLatchEntity {
+ private CountDownLatch lock = new CountDownLatch(1);
+ private volatile boolean isBlocked;
+
+ @Override
+ public void unblock() {
+ lock.countDown();
+ }
+
+ @Override
+ protected void postStop() {
+ super.preStop();
+ try {
+ isBlocked = true;
+ lock.await();
+ isBlocked = false;
+ } catch (InterruptedException e) {
+ throw Exceptions.propagate(e);
+ }
+ }
+
+ @Override
+ public Class<?> getDriverInterface() {
+ return EmptySoftwareProcessDriver.class;
+ }
+
+ @Override
+ public boolean isBlocked() {
+ return isBlocked;
+ }
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/ServerShutdownTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/ServerShutdownTest.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/ServerShutdownTest.java
new file mode 100644
index 0000000..9d55482
--- /dev/null
+++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/ServerShutdownTest.java
@@ -0,0 +1,164 @@
+/*
+ * 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.assertNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedHashMap;
+import javax.ws.rs.core.MultivaluedMap;
+
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.mgmt.EntityManager;
+import org.apache.brooklyn.api.mgmt.Task;
+import org.apache.brooklyn.core.entity.Attributes;
+import org.apache.brooklyn.core.entity.EntityAsserts;
+import org.apache.brooklyn.core.entity.drivers.BasicEntityDriverManager;
+import org.apache.brooklyn.core.entity.drivers.ReflectiveEntityDriverFactory;
+import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
+import org.apache.brooklyn.core.entity.trait.Startable;
+import org.apache.brooklyn.core.test.entity.TestApplication;
+import org.apache.brooklyn.rest.resources.ServerResourceTest.StopLatchEntity;
+import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+@Test( // by using a different suite name we disallow interleaving other tests between the methods of this test class, which wrecks the test fixtures
+ suiteName = "ServerShutdownTest")
+public class ServerShutdownTest extends BrooklynRestResourceTest {
+ private static final Logger log = LoggerFactory.getLogger(ServerResourceTest.class);
+
+ @Override
+ protected boolean isMethodInit() {
+ return true;
+ }
+
+ @Test
+ public void testShutdown() throws Exception {
+ assertTrue(getManagementContext().isRunning());
+ assertFalse(shutdownListener.isRequested());
+
+ MultivaluedMap<String, String> formData = new MultivaluedHashMap();
+ formData.add("requestTimeout", "0");
+ formData.add("delayForHttpReturn", "0");
+ client().path("/server/shutdown").type(MediaType.APPLICATION_FORM_URLENCODED).post(formData);
+
+ Asserts.succeedsEventually(new Runnable() {
+ @Override
+ public void run() {
+ assertTrue(shutdownListener.isRequested());
+ }
+ });
+ Asserts.succeedsEventually(new Runnable() {
+ @Override public void run() {
+ assertFalse(getManagementContext().isRunning());
+ }});
+ }
+
+ @Test
+ public void testStopAppThenShutdownAndStopAppsWaitsForFirstStop() throws InterruptedException {
+ ReflectiveEntityDriverFactory f = ((BasicEntityDriverManager)getManagementContext().getEntityDriverManager()).getReflectiveDriverFactory();
+ f.addClassFullNameMapping("org.apache.brooklyn.entity.software.base.EmptySoftwareProcessDriver", "org.apache.brooklyn.rest.resources.ServerResourceTest$EmptySoftwareProcessTestDriver");
+
+ // Second stop on SoftwareProcess could return early, while the first stop is still in progress
+ // This causes the app to shutdown prematurely, leaking machines.
+ EntityManager emgr = getManagementContext().getEntityManager();
+ EntitySpec<TestApplication> appSpec = EntitySpec.create(TestApplication.class);
+ TestApplication app = emgr.createEntity(appSpec);
+ EntitySpec<StopLatchEntity> latchEntitySpec = EntitySpec.create(StopLatchEntity.class);
+ final StopLatchEntity entity = app.createAndManageChild(latchEntitySpec);
+ app.start(ImmutableSet.of(app.newLocalhostProvisioningLocation()));
+ EntityAsserts.assertAttributeEquals(entity, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING);
+
+ try {
+ final Task<Void> firstStop = app.invoke(Startable.STOP, ImmutableMap.<String, Object>of());
+ Asserts.succeedsEventually(new Runnable() {
+ @Override
+ public void run() {
+ assertTrue(entity.isBlocked());
+ }
+ });
+
+ final AtomicReference<Exception> shutdownError = new AtomicReference<>();
+ // Can't use ExecutionContext as it will be stopped on shutdown
+ Thread shutdownThread = new Thread() {
+ @Override
+ public void run() {
+ try {
+ MultivaluedMap<String, String> formData = new MultivaluedHashMap<>();
+ formData.add("stopAppsFirst", "true");
+ formData.add("shutdownTimeout", "0");
+ formData.add("requestTimeout", "0");
+ formData.add("delayForHttpReturn", "0");
+ client().path("/server/shutdown").type(MediaType.APPLICATION_FORM_URLENCODED).post(formData);
+ } catch (Exception e) {
+ log.error("Shutdown request error", e);
+ shutdownError.set(e);
+ throw Exceptions.propagate(e);
+ }
+ }
+ };
+ shutdownThread.start();
+
+ //shutdown must wait until the first stop completes (or time out)
+ Asserts.succeedsContinually(new Runnable() {
+ @Override
+ public void run() {
+ assertFalse(firstStop.isDone());
+ assertEquals(getManagementContext().getApplications().size(), 1);
+ assertFalse(shutdownListener.isRequested());
+ }
+ });
+
+ // NOTE test is not fully deterministic. Depending on thread scheduling this will
+ // execute before or after ServerResource.shutdown does the app stop loop. This
+ // means that the shutdown code might not see the app at all. In any case though
+ // the test must succeed.
+ entity.unblock();
+
+ Asserts.succeedsEventually(new Runnable() {
+ @Override
+ public void run() {
+ assertTrue(firstStop.isDone());
+ assertTrue(shutdownListener.isRequested());
+ assertFalse(getManagementContext().isRunning());
+ }
+ });
+
+ shutdownThread.join();
+ assertNull(shutdownError.get(), "Shutdown request error, logged above");
+ } finally {
+ // Be sure we always unblock entity stop even in the case of an exception.
+ // In the success path the entity is already unblocked above.
+ entity.unblock();
+ }
+ }
+
+}