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:40 UTC
[22/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/resources/UsageResourceTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/resources/UsageResourceTest.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/resources/UsageResourceTest.java
new file mode 100644
index 0000000..72392fe
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/resources/UsageResourceTest.java
@@ -0,0 +1,443 @@
+/*
+ * 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.util.Arrays;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
+
+import javax.ws.rs.core.Response;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.api.location.NoMachinesAvailableException;
+import org.apache.brooklyn.core.mgmt.internal.LocalUsageManager;
+import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.test.entity.TestApplication;
+import org.apache.brooklyn.entity.software.base.SoftwareProcessEntityTest;
+import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+import org.apache.brooklyn.rest.domain.ApplicationSpec;
+import org.apache.brooklyn.rest.domain.Status;
+import org.apache.brooklyn.rest.domain.TaskSummary;
+import org.apache.brooklyn.rest.domain.UsageStatistic;
+import org.apache.brooklyn.rest.domain.UsageStatistics;
+import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest;
+import org.apache.brooklyn.rest.testing.mocks.RestMockSimpleEntity;
+import org.apache.brooklyn.util.repeat.Repeater;
+import org.apache.brooklyn.util.time.Time;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.GenericType;
+
+public class UsageResourceTest extends BrooklynRestResourceTest {
+
+ @SuppressWarnings("unused")
+ private static final Logger LOG = LoggerFactory.getLogger(UsageResourceTest.class);
+
+ private static final long TIMEOUT_MS = 10*1000;
+
+ private Calendar testStartTime;
+
+ private final ApplicationSpec simpleSpec = ApplicationSpec.builder().name("simple-app").
+ entities(ImmutableSet.of(new org.apache.brooklyn.rest.domain.EntitySpec("simple-ent", RestMockSimpleEntity.class.getName()))).
+ locations(ImmutableSet.of("localhost")).
+ build();
+
+ @BeforeMethod(alwaysRun=true)
+ public void setUpMethod() {
+ ((ManagementContextInternal)getManagementContext()).getStorage().remove(LocalUsageManager.APPLICATION_USAGE_KEY);
+ ((ManagementContextInternal)getManagementContext()).getStorage().remove(LocalUsageManager.LOCATION_USAGE_KEY);
+ testStartTime = new GregorianCalendar();
+ }
+
+ @Test
+ public void testListApplicationUsages() throws Exception {
+ // Create an app
+ Calendar preStart = new GregorianCalendar();
+ String appId = createApp(simpleSpec);
+ Calendar postStart = new GregorianCalendar();
+
+ // We will retrieve usage from one millisecond after start; this guarantees to not be
+ // told about both STARTING+RUNNING, which could otherwise happen if they are in the
+ // same milliscond.
+ Calendar afterPostStart = Time.newCalendarFromMillisSinceEpochUtc(postStart.getTime().getTime()+1);
+
+ // Check that app's usage is returned
+ ClientResponse response = client().resource("/v1/usage/applications").get(ClientResponse.class);
+ assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
+ Iterable<UsageStatistics> usages = response.getEntity(new GenericType<List<UsageStatistics>>() {});
+ UsageStatistics usage = Iterables.getOnlyElement(usages);
+ assertAppUsage(usage, appId, ImmutableList.of(Status.STARTING, Status.RUNNING), roundDown(preStart), postStart);
+
+ // check app ignored if endCalendar before app started
+ response = client().resource("/v1/usage/applications?start="+0+"&end="+(preStart.getTime().getTime()-1)).get(ClientResponse.class);
+ assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
+ usages = response.getEntity(new GenericType<List<UsageStatistics>>() {});
+ assertTrue(Iterables.isEmpty(usages), "usages="+usages);
+
+ // Wait, so that definitely asking about things that have happened (not things in the future,
+ // or events that are happening this exact same millisecond)
+ waitForFuture(afterPostStart.getTime().getTime());
+
+ // Check app start + end date truncated, even if running for longer (i.e. only tell us about this time window).
+ // Note that start==end means we get a snapshot of the apps in use at that exact time.
+ //
+ // The start/end times in UsageStatistic are in String format, and are rounded down to the nearest second.
+ // The comparison does use the milliseconds passed in the REST call though.
+ // The rounding down result should be the same as roundDown(afterPostStart), because that is the time-window
+ // we asked for.
+ response = client().resource("/v1/usage/applications?start="+afterPostStart.getTime().getTime()+"&end="+afterPostStart.getTime().getTime()).get(ClientResponse.class);
+ assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
+ usages = response.getEntity(new GenericType<List<UsageStatistics>>() {});
+ usage = Iterables.getOnlyElement(usages);
+ assertAppUsage(usage, appId, ImmutableList.of(Status.RUNNING), roundDown(preStart), postStart);
+ assertAppUsageTimesTruncated(usage, roundDown(afterPostStart), roundDown(afterPostStart));
+
+ // Delete the app
+ Calendar preDelete = new GregorianCalendar();
+ deleteApp(appId);
+ Calendar postDelete = new GregorianCalendar();
+
+ // Deleted app still returned, if in time range
+ response = client().resource("/v1/usage/applications").get(ClientResponse.class);
+ assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
+ usages = response.getEntity(new GenericType<List<UsageStatistics>>() {});
+ usage = Iterables.getOnlyElement(usages);
+ assertAppUsage(usage, appId, ImmutableList.of(Status.STARTING, Status.RUNNING, Status.DESTROYED), roundDown(preStart), postDelete);
+ assertAppUsage(ImmutableList.copyOf(usage.getStatistics()).subList(2, 3), appId, ImmutableList.of(Status.DESTROYED), roundDown(preDelete), postDelete);
+
+ long afterPostDelete = postDelete.getTime().getTime()+1;
+ waitForFuture(afterPostDelete);
+
+ response = client().resource("/v1/usage/applications?start=" + afterPostDelete).get(ClientResponse.class);
+ assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
+ usages = response.getEntity(new GenericType<List<UsageStatistics>>() {});
+ assertTrue(Iterables.isEmpty(usages), "usages="+usages);
+ }
+
+ @Test
+ public void testGetApplicationUsagesForNonExistantApp() throws Exception {
+ ClientResponse response = client().resource("/v1/usage/applications/wrongid").get(ClientResponse.class);
+ assertEquals(response.getStatus(), Response.Status.NOT_FOUND.getStatusCode());
+ }
+
+ @Test
+ public void testGetApplicationUsage() throws Exception {
+ // Create an app
+ Calendar preStart = new GregorianCalendar();
+ String appId = createApp(simpleSpec);
+ Calendar postStart = new GregorianCalendar();
+
+ // Normal request returns all
+ ClientResponse response = client().resource("/v1/usage/applications/" + appId).get(ClientResponse.class);
+ assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
+ UsageStatistics usage = response.getEntity(new GenericType<UsageStatistics>() {});
+ assertAppUsage(usage, appId, ImmutableList.of(Status.STARTING, Status.RUNNING), roundDown(preStart), postStart);
+
+ // Time-constrained requests
+ response = client().resource("/v1/usage/applications/" + appId + "?start=1970-01-01T00:00:00-0100").get(ClientResponse.class);
+ usage = response.getEntity(new GenericType<UsageStatistics>() {});
+ assertAppUsage(usage, appId, ImmutableList.of(Status.STARTING, Status.RUNNING), roundDown(preStart), postStart);
+
+ response = client().resource("/v1/usage/applications/" + appId + "?start=9999-01-01T00:00:00+0100").get(ClientResponse.class);
+ assertTrue(response.getStatus() >= 400, "end defaults to NOW, so future start should fail, instead got code "+response.getStatus());
+
+ response = client().resource("/v1/usage/applications/" + appId + "?start=9999-01-01T00:00:00%2B0100&end=9999-01-02T00:00:00%2B0100").get(ClientResponse.class);
+ usage = response.getEntity(new GenericType<UsageStatistics>() {});
+ assertTrue(usage.getStatistics().isEmpty());
+
+ response = client().resource("/v1/usage/applications/" + appId + "?end=9999-01-01T00:00:00+0100").get(ClientResponse.class);
+ usage = response.getEntity(new GenericType<UsageStatistics>() {});
+ assertAppUsage(usage, appId, ImmutableList.of(Status.STARTING, Status.RUNNING), roundDown(preStart), postStart);
+
+ response = client().resource("/v1/usage/applications/" + appId + "?start=9999-01-01T00:00:00+0100&end=9999-02-01T00:00:00+0100").get(ClientResponse.class);
+ usage = response.getEntity(new GenericType<UsageStatistics>() {});
+ assertTrue(usage.getStatistics().isEmpty());
+
+ response = client().resource("/v1/usage/applications/" + appId + "?start=1970-01-01T00:00:00-0100&end=9999-01-01T00:00:00+0100").get(ClientResponse.class);
+ usage = response.getEntity(new GenericType<UsageStatistics>() {});
+ assertAppUsage(usage, appId, ImmutableList.of(Status.STARTING, Status.RUNNING), roundDown(preStart), postStart);
+
+ response = client().resource("/v1/usage/applications/" + appId + "?end=1970-01-01T00:00:00-0100").get(ClientResponse.class);
+ usage = response.getEntity(new GenericType<UsageStatistics>() {});
+ assertTrue(usage.getStatistics().isEmpty());
+
+ // Delete the app
+ Calendar preDelete = new GregorianCalendar();
+ deleteApp(appId);
+ Calendar postDelete = new GregorianCalendar();
+
+ // Deleted app still returned, if in time range
+ response = client().resource("/v1/usage/applications/" + appId).get(ClientResponse.class);
+ assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
+ usage = response.getEntity(new GenericType<UsageStatistics>() {});
+ assertAppUsage(usage, appId, ImmutableList.of(Status.STARTING, Status.RUNNING, Status.DESTROYED), roundDown(preStart), postDelete);
+ assertAppUsage(ImmutableList.copyOf(usage.getStatistics()).subList(2, 3), appId, ImmutableList.of(Status.DESTROYED), roundDown(preDelete), postDelete);
+
+ // Deleted app not returned if terminated before time range begins
+ long afterPostDelete = postDelete.getTime().getTime()+1;
+ waitForFuture(afterPostDelete);
+ response = client().resource("/v1/usage/applications/" + appId +"?start=" + afterPostDelete).get(ClientResponse.class);
+ assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
+ usage = response.getEntity(new GenericType<UsageStatistics>() {});
+ assertTrue(usage.getStatistics().isEmpty(), "usages="+usage);
+ }
+
+ @Test
+ public void testGetMachineUsagesForNonExistantMachine() throws Exception {
+ ClientResponse response = client().resource("/v1/usage/machines/wrongid").get(ClientResponse.class);
+ assertEquals(response.getStatus(), Response.Status.NOT_FOUND.getStatusCode());
+ }
+
+ @Test
+ public void testGetMachineUsagesInitiallyEmpty() throws Exception {
+ // All machines: empty
+ ClientResponse response = client().resource("/v1/usage/machines").get(ClientResponse.class);
+ assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
+ Iterable<UsageStatistics> usages = response.getEntity(new GenericType<List<UsageStatistics>>() {});
+ assertTrue(Iterables.isEmpty(usages));
+
+ // Specific machine that does not exist: get 404
+ response = client().resource("/v1/usage/machines/machineIdThatDoesNotExist").get(ClientResponse.class);
+ assertEquals(response.getStatus(), Response.Status.NOT_FOUND.getStatusCode());
+ }
+
+ @Test
+ public void testListAndGetMachineUsage() throws Exception {
+ Location location = getManagementContext().getLocationManager().createLocation(LocationSpec.create(DynamicLocalhostMachineProvisioningLocation.class));
+ TestApplication app = getManagementContext().getEntityManager().createEntity(EntitySpec.create(TestApplication.class));
+ SoftwareProcessEntityTest.MyService entity = app.createAndManageChild(org.apache.brooklyn.api.entity.EntitySpec.create(SoftwareProcessEntityTest.MyService.class));
+
+ Calendar preStart = new GregorianCalendar();
+ app.start(ImmutableList.of(location));
+ Calendar postStart = new GregorianCalendar();
+ Location machine = Iterables.getOnlyElement(entity.getLocations());
+
+ // All machines
+ ClientResponse response = client().resource("/v1/usage/machines").get(ClientResponse.class);
+ assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
+ Iterable<UsageStatistics> usages = response.getEntity(new GenericType<List<UsageStatistics>>() {});
+ UsageStatistics usage = Iterables.getOnlyElement(usages);
+ assertMachineUsage(usage, app.getId(), machine.getId(), ImmutableList.of(Status.ACCEPTED), roundDown(preStart), postStart);
+
+ // Specific machine
+ response = client().resource("/v1/usage/machines/"+machine.getId()).get(ClientResponse.class);
+ assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
+ assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
+ usage = response.getEntity(new GenericType<UsageStatistics>() {});
+ assertMachineUsage(usage, app.getId(), machine.getId(), ImmutableList.of(Status.ACCEPTED), roundDown(preStart), postStart);
+ }
+
+ @Test
+ public void testListMachinesUsageForApp() throws Exception {
+ Location location = getManagementContext().getLocationManager().createLocation(LocationSpec.create(DynamicLocalhostMachineProvisioningLocation.class));
+ TestApplication app = getManagementContext().getEntityManager().createEntity(EntitySpec.create(TestApplication.class));
+ SoftwareProcessEntityTest.MyService entity = app.createAndManageChild(org.apache.brooklyn.api.entity.EntitySpec.create(SoftwareProcessEntityTest.MyService.class));
+ String appId = app.getId();
+
+ Calendar preStart = new GregorianCalendar();
+ app.start(ImmutableList.of(location));
+ Calendar postStart = new GregorianCalendar();
+ Location machine = Iterables.getOnlyElement(entity.getLocations());
+
+ // For running machine
+ ClientResponse response = client().resource("/v1/usage/machines?application="+appId).get(ClientResponse.class);
+ assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
+ Iterable<UsageStatistics> usages = response.getEntity(new GenericType<List<UsageStatistics>>() {});
+ UsageStatistics usage = Iterables.getOnlyElement(usages);
+ assertMachineUsage(usage, app.getId(), machine.getId(), ImmutableList.of(Status.ACCEPTED), roundDown(preStart), postStart);
+
+ // Stop the machine
+ Calendar preStop = new GregorianCalendar();
+ app.stop();
+ Calendar postStop = new GregorianCalendar();
+
+ // Deleted machine still returned, if in time range
+ response = client().resource("/v1/usage/machines?application=" + appId).get(ClientResponse.class);
+ assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
+ usages = response.getEntity(new GenericType<List<UsageStatistics>>() {});
+ usage = Iterables.getOnlyElement(usages);
+ assertMachineUsage(usage, app.getId(), machine.getId(), ImmutableList.of(Status.ACCEPTED, Status.DESTROYED), roundDown(preStart), postStop);
+ assertMachineUsage(ImmutableList.copyOf(usage.getStatistics()).subList(1,2), appId, machine.getId(), ImmutableList.of(Status.DESTROYED), roundDown(preStop), postStop);
+
+ // Terminated machines ignored if terminated since start-time
+ long futureTime = postStop.getTime().getTime()+1;
+ waitForFuture(futureTime);
+ response = client().resource("/v1/usage/applications?start=" + futureTime).get(ClientResponse.class);
+ assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
+ usages = response.getEntity(new GenericType<List<UsageStatistics>>() {});
+ assertTrue(Iterables.isEmpty(usages), "usages="+usages);
+ }
+
+ private String createApp(ApplicationSpec spec) {
+ ClientResponse response = clientDeploy(spec);
+ assertEquals(response.getStatus(), Response.Status.CREATED.getStatusCode());
+ TaskSummary createTask = response.getEntity(TaskSummary.class);
+ waitForTask(createTask.getId());
+ return createTask.getEntityId();
+ }
+
+ private void deleteApp(String appId) {
+ ClientResponse response = client().resource("/v1/applications/"+appId)
+ .delete(ClientResponse.class);
+ assertEquals(response.getStatus(), Response.Status.ACCEPTED.getStatusCode());
+ TaskSummary deletionTask = response.getEntity(TaskSummary.class);
+ waitForTask(deletionTask.getId());
+ }
+
+ private void assertCalendarOrders(Object context, Calendar... Calendars) {
+ if (Calendars.length <= 1) return;
+
+ long[] times = new long[Calendars.length];
+ for (int i = 0; i < times.length; i++) {
+ times[i] = millisSinceStart(Calendars[i]);
+ }
+ String err = "context="+context+"; Calendars="+Arrays.toString(Calendars) + "; CalendarsSanitized="+Arrays.toString(times);
+
+ Calendar Calendar = Calendars[0];
+ for (int i = 1; i < Calendars.length; i++) {
+ assertTrue(Calendar.getTime().getTime() <= Calendars[i].getTime().getTime(), err);
+ }
+ }
+
+ private void waitForTask(final String taskId) {
+ boolean success = Repeater.create()
+ .repeat(new Runnable() { public void run() {}})
+ .until(new Callable<Boolean>() {
+ @Override public Boolean call() {
+ ClientResponse response = client().resource("/v1/activities/"+taskId).get(ClientResponse.class);
+ if (response.getStatus() == Response.Status.NOT_FOUND.getStatusCode()) {
+ return true;
+ }
+ TaskSummary summary = response.getEntity(TaskSummary.class);
+ return summary != null && summary.getEndTimeUtc() != null;
+ }})
+ .every(10L, TimeUnit.MILLISECONDS)
+ .limitTimeTo(TIMEOUT_MS, TimeUnit.MILLISECONDS)
+ .run();
+ assertTrue(success, "task "+taskId+" not finished");
+ }
+
+ private long millisSinceStart(Calendar time) {
+ return time.getTime().getTime() - testStartTime.getTime().getTime();
+ }
+
+ private Calendar roundDown(Calendar calendar) {
+ long time = calendar.getTime().getTime();
+ long timeDown = ((long)(time / 1000)) * 1000;
+ return Time.newCalendarFromMillisSinceEpochUtc(timeDown);
+ }
+
+ @SuppressWarnings("unused")
+ private Calendar roundUp(Calendar calendar) {
+ long time = calendar.getTime().getTime();
+ long timeDown = ((long)(time / 1000)) * 1000;
+ long timeUp = (time == timeDown) ? time : timeDown + 1000;
+ return Time.newCalendarFromMillisSinceEpochUtc(timeUp);
+ }
+
+ private void assertMachineUsage(UsageStatistics usage, String appId, String machineId, List<Status> states, Calendar pre, Calendar post) throws Exception {
+ assertUsage(usage.getStatistics(), appId, machineId, states, pre, post, false);
+ }
+
+ private void assertMachineUsage(Iterable<UsageStatistic> usages, String appId, String machineId, List<Status> states, Calendar pre, Calendar post) throws Exception {
+ assertUsage(usages, appId, machineId, states, pre, post, false);
+ }
+
+ private void assertAppUsage(UsageStatistics usage, String appId, List<Status> states, Calendar pre, Calendar post) throws Exception {
+ assertUsage(usage.getStatistics(), appId, appId, states, pre, post, false);
+ }
+
+ private void assertAppUsage(Iterable<UsageStatistic> usages, String appId, List<Status> states, Calendar pre, Calendar post) throws Exception {
+ assertUsage(usages, appId, appId, states, pre, post, false);
+ }
+
+ private void assertUsage(Iterable<UsageStatistic> usages, String appId, String id, List<Status> states, Calendar pre, Calendar post, boolean allowGaps) throws Exception {
+ String errMsg = "usages="+usages;
+ Calendar now = new GregorianCalendar();
+ Calendar lowerBound = pre;
+ Calendar strictStart = null;
+
+ assertEquals(Iterables.size(usages), states.size(), errMsg);
+ for (int i = 0; i < Iterables.size(usages); i++) {
+ UsageStatistic usage = Iterables.get(usages, i);
+ Calendar usageStart = Time.parseCalendar(usage.getStart());
+ Calendar usageEnd = Time.parseCalendar(usage.getEnd());
+ assertEquals(usage.getId(), id, errMsg);
+ assertEquals(usage.getApplicationId(), appId, errMsg);
+ assertEquals(usage.getStatus(), states.get(i), errMsg);
+ assertCalendarOrders(usages, lowerBound, usageStart, post);
+ assertCalendarOrders(usages, usageEnd, now);
+ if (strictStart != null) {
+ assertEquals(usageStart, strictStart, errMsg);
+ }
+ if (!allowGaps) {
+ strictStart = usageEnd;
+ }
+ lowerBound = usageEnd;
+ }
+ }
+
+ private void assertAppUsageTimesTruncated(UsageStatistics usages, Calendar strictStart, Calendar strictEnd) throws Exception {
+ String errMsg = "strictStart="+Time.makeDateString(strictStart)+"; strictEnd="+Time.makeDateString(strictEnd)+";usages="+usages;
+ Calendar usageStart = Time.parseCalendar(Iterables.getFirst(usages.getStatistics(), null).getStart());
+ Calendar usageEnd = Time.parseCalendar(Iterables.getLast(usages.getStatistics()).getStart());
+ // time zones might be different - so must convert to date
+ assertEquals(usageStart.getTime(), strictStart.getTime(), "usageStart="+Time.makeDateString(usageStart)+";"+errMsg);
+ assertEquals(usageEnd.getTime(), strictEnd.getTime(), errMsg);
+ }
+
+ public static class DynamicLocalhostMachineProvisioningLocation extends LocalhostMachineProvisioningLocation {
+ @Override
+ public SshMachineLocation obtain(Map<?, ?> flags) throws NoMachinesAvailableException {
+ return super.obtain(flags);
+ }
+
+ @Override
+ public void release(SshMachineLocation machine) {
+ super.release(machine);
+ super.machines.remove(machine);
+ getManagementContext().getLocationManager().unmanage(machine);
+ }
+ }
+
+ private void waitForFuture(long futureTime) throws InterruptedException {
+ long now;
+ while ((now = System.currentTimeMillis()) < futureTime) {
+ Thread.sleep(futureTime - now);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/resources/VersionResourceTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/resources/VersionResourceTest.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/resources/VersionResourceTest.java
new file mode 100644
index 0000000..384feb0
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/resources/VersionResourceTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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 javax.ws.rs.core.Response;
+
+import org.testng.annotations.Test;
+
+import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest;
+
+import com.sun.jersey.api.client.ClientResponse;
+
+public class VersionResourceTest extends BrooklynRestResourceTest {
+
+ @Test
+ public void testGetVersion() {
+ ClientResponse response = client().resource("/v1/version")
+ .get(ClientResponse.class);
+
+ assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
+ String version = response.getEntity(String.class);
+// TODO johnmccabe - 19/12/2015 :: temporarily disabled while the repo split work is ongoing,
+// must be restored when switching back to a valid brooklyn version
+// assertTrue(version.matches("^\\d+\\.\\d+\\.\\d+.*"));
+ assertTrue(true);
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ protected void addBrooklynResources() {
+ addResource(new VersionResource());
+ }
+}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/security/PasswordHasherTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/security/PasswordHasherTest.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/security/PasswordHasherTest.java
new file mode 100644
index 0000000..575d6a4
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/security/PasswordHasherTest.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.security;
+
+import static org.testng.Assert.assertEquals;
+
+import org.testng.annotations.Test;
+
+public class PasswordHasherTest {
+
+ @Test
+ public void testHashSha256() throws Exception {
+ // Note: expected hash values generated externally:
+ // echo -n mysaltmypassword | openssl dgst -sha256
+
+ assertEquals(PasswordHasher.sha256("mysalt", "mypassword"), "d02878b06efa88579cd84d9e50b211c0a7caa92cf243bad1622c66081f7e2692");
+ assertEquals(PasswordHasher.sha256("", "mypassword"), "89e01536ac207279409d4de1e5253e01f4a1769e696db0d6062ca9b8f56767c8");
+ assertEquals(PasswordHasher.sha256(null, "mypassword"), "89e01536ac207279409d4de1e5253e01f4a1769e696db0d6062ca9b8f56767c8");
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/security/provider/TestSecurityProvider.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/security/provider/TestSecurityProvider.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/security/provider/TestSecurityProvider.java
new file mode 100644
index 0000000..cad251f
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/security/provider/TestSecurityProvider.java
@@ -0,0 +1,46 @@
+/*
+ * 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.security.provider;
+
+import javax.servlet.http.HttpSession;
+
+import org.apache.http.auth.UsernamePasswordCredentials;
+
+public class TestSecurityProvider implements SecurityProvider {
+
+ public static final String USER = "test";
+ public static final String PASSWORD = "opensesame";
+ public static final UsernamePasswordCredentials CREDENTIAL =
+ new UsernamePasswordCredentials(TestSecurityProvider.USER, TestSecurityProvider.PASSWORD);
+
+ @Override
+ public boolean isAuthenticated(HttpSession session) {
+ return false;
+ }
+
+ @Override
+ public boolean authenticate(HttpSession session, String user, String password) {
+ return USER.equals(user) && PASSWORD.equals(password);
+ }
+
+ @Override
+ public boolean logout(HttpSession session) {
+ return false;
+ }
+}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/test/config/render/TestRendererHints.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/test/config/render/TestRendererHints.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/test/config/render/TestRendererHints.java
new file mode 100644
index 0000000..91294db
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/test/config/render/TestRendererHints.java
@@ -0,0 +1,36 @@
+/*
+ * 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.test.config.render;
+
+import org.apache.brooklyn.core.config.render.RendererHints;
+
+/** Methods used when testing the {@link RendererHints} regiostry. */
+public class TestRendererHints {
+
+ /** Clear the registry.
+ *
+ * MUST be used by a single test only.
+ * TestNG interleaves the tests (sequentially) which results in tearDown
+ * executing in the middle of another class' tests. Only one tearDown may
+ * call this method.
+ **/
+ public static void clearRegistry() {
+ RendererHints.getRegistry().clear();
+ }
+}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/test/entity/brooklynnode/DeployBlueprintTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/test/entity/brooklynnode/DeployBlueprintTest.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/test/entity/brooklynnode/DeployBlueprintTest.java
new file mode 100644
index 0000000..2ab62a9
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/test/entity/brooklynnode/DeployBlueprintTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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.test.entity.brooklynnode;
+
+import static org.testng.Assert.assertEquals;
+
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.mgmt.EntityManager;
+import org.apache.brooklyn.entity.brooklynnode.BrooklynNode;
+import org.apache.brooklyn.entity.brooklynnode.BrooklynNode.DeployBlueprintEffector;
+import org.apache.brooklyn.entity.stock.BasicApplication;
+import org.apache.brooklyn.feed.http.JsonFunctions;
+import org.apache.brooklyn.rest.BrooklynRestApiLauncherTestFixture;
+import org.apache.brooklyn.test.HttpTestUtils;
+import org.apache.brooklyn.util.guava.Functionals;
+import org.eclipse.jetty.server.Server;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class DeployBlueprintTest extends BrooklynRestApiLauncherTestFixture {
+
+ private static final Logger log = LoggerFactory.getLogger(DeployBlueprintTest.class);
+
+ Server server;
+
+ @BeforeMethod(alwaysRun=true)
+ public void setUp() throws Exception {
+ server = newServer();
+ useServerForTest(server);
+ }
+
+ @Test
+ public void testStartsAppViaEffector() throws Exception {
+ URI webConsoleUri = URI.create(getBaseUri());
+
+ EntitySpec<BrooklynNode> spec = EntitySpec.create(BrooklynNode.class);
+ EntityManager mgr = getManagementContextFromJettyServerAttributes(server).getEntityManager();
+ BrooklynNode node = mgr.createEntity(spec);
+ node.sensors().set(BrooklynNode.WEB_CONSOLE_URI, webConsoleUri);
+ mgr.manage(node);
+ Map<String, String> params = ImmutableMap.of(DeployBlueprintEffector.BLUEPRINT_CAMP_PLAN.getName(), "{ services: [ serviceType: \"java:"+BasicApplication.class.getName()+"\" ] }");
+ String id = node.invoke(BrooklynNode.DEPLOY_BLUEPRINT, params).getUnchecked();
+
+ log.info("got: "+id);
+
+ String apps = HttpTestUtils.getContent(webConsoleUri.toString()+"/v1/applications");
+ List<String> appType = parseJsonList(apps, ImmutableList.of("spec", "type"), String.class);
+ assertEquals(appType, ImmutableList.of(BasicApplication.class.getName()));
+
+ String status = HttpTestUtils.getContent(webConsoleUri.toString()+"/v1/applications/"+id+"/entities/"+id+"/sensors/service.status");
+ log.info("STATUS: "+status);
+ }
+
+ private <T> List<T> parseJsonList(String json, List<String> elements, Class<T> clazz) {
+ Function<String, List<T>> func = Functionals.chain(
+ JsonFunctions.asJson(),
+ JsonFunctions.forEach(Functionals.chain(
+ JsonFunctions.walk(elements),
+ JsonFunctions.cast(clazz))));
+ return func.apply(json);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestApiTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestApiTest.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestApiTest.java
new file mode 100644
index 0000000..bf3f8af
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestApiTest.java
@@ -0,0 +1,223 @@
+/*
+ * 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.testing;
+
+import java.net.URI;
+
+import org.apache.brooklyn.api.location.LocationRegistry;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.camp.brooklyn.BrooklynCampPlatformLauncherNoServer;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.location.BasicLocationRegistry;
+import org.apache.brooklyn.core.mgmt.ManagementContextInjectable;
+import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
+import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
+import org.apache.brooklyn.rest.BrooklynRestApi;
+import org.apache.brooklyn.rest.BrooklynRestApiLauncherTest;
+import org.apache.brooklyn.rest.util.BrooklynRestResourceUtils;
+import org.apache.brooklyn.rest.util.ManagementContextProvider;
+import org.apache.brooklyn.rest.util.NoOpRecordingShutdownHandler;
+import org.apache.brooklyn.rest.util.NullHttpServletRequestProvider;
+import org.apache.brooklyn.rest.util.NullServletConfigProvider;
+import org.apache.brooklyn.rest.util.ShutdownHandlerProvider;
+import org.apache.brooklyn.rest.util.json.BrooklynJacksonJsonProvider;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeMethod;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.base.Preconditions;
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.api.core.DefaultResourceConfig;
+import com.sun.jersey.test.framework.AppDescriptor;
+import com.sun.jersey.test.framework.JerseyTest;
+import com.sun.jersey.test.framework.LowLevelAppDescriptor;
+import com.sun.jersey.test.framework.spi.container.TestContainer;
+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.inmemory.InMemoryTestContainerFactory;
+
+public abstract class BrooklynRestApiTest {
+
+ protected ManagementContext manager;
+
+ protected boolean useLocalScannedCatalog = false;
+ protected NoOpRecordingShutdownHandler shutdownListener = createShutdownHandler();
+
+ @BeforeMethod(alwaysRun = true)
+ public void setUpMethod() {
+ shutdownListener.reset();
+ }
+
+ protected synchronized void useLocalScannedCatalog() {
+ if (manager!=null && !useLocalScannedCatalog)
+ throw new IllegalStateException("useLocalScannedCatalog must be specified before manager is accessed/created");
+ useLocalScannedCatalog = true;
+ }
+
+ private NoOpRecordingShutdownHandler createShutdownHandler() {
+ return new NoOpRecordingShutdownHandler();
+ }
+
+ protected synchronized ManagementContext getManagementContext() {
+ if (manager==null) {
+ if (useLocalScannedCatalog) {
+ manager = new LocalManagementContext();
+ BrooklynRestApiLauncherTest.forceUseOfDefaultCatalogWithJavaClassPath(manager);
+ } else {
+ manager = new LocalManagementContextForTests();
+ }
+ manager.getHighAvailabilityManager().disabled();
+ BasicLocationRegistry.setupLocationRegistryForTesting(manager);
+
+ new BrooklynCampPlatformLauncherNoServer()
+ .useManagementContext(manager)
+ .launch();
+ }
+ return manager;
+ }
+
+ protected ObjectMapper mapper() {
+ return BrooklynJacksonJsonProvider.findSharedObjectMapper(null, getManagementContext());
+ }
+
+ @AfterClass
+ public void tearDown() throws Exception {
+ destroyManagementContext();
+ }
+
+ protected void destroyManagementContext() {
+ if (manager!=null) {
+ Entities.destroyAll(manager);
+ manager = null;
+ }
+ }
+
+ public LocationRegistry getLocationRegistry() {
+ return new BrooklynRestResourceUtils(getManagementContext()).getLocationRegistry();
+ }
+
+ private JerseyTest jerseyTest;
+ protected DefaultResourceConfig config = new DefaultResourceConfig();
+
+ protected final void addResource(Object resource) {
+ Preconditions.checkNotNull(config, "Must run before setUpJersey");
+
+ if (resource instanceof Class)
+ config.getClasses().add((Class<?>)resource);
+ else
+ config.getSingletons().add(resource);
+
+ if (resource instanceof ManagementContextInjectable) {
+ ((ManagementContextInjectable)resource).setManagementContext(getManagementContext());
+ }
+ }
+
+ protected final void addProvider(Class<?> provider) {
+ Preconditions.checkNotNull(config, "Must run before setUpJersey");
+
+ config.getClasses().add(provider);
+ }
+
+ protected void addDefaultResources() {
+ // seems we have to provide our own injector because the jersey test framework
+ // doesn't inject ServletConfig and it all blows up
+ // and the servlet config provider must be an instance; addClasses doesn't work for some reason
+ addResource(new NullServletConfigProvider());
+ addResource(new ManagementContextProvider(getManagementContext()));
+ addProvider(NullHttpServletRequestProvider.class);
+ addResource(new ShutdownHandlerProvider(shutdownListener));
+ }
+
+ protected final void setUpResources() {
+ addDefaultResources();
+ addBrooklynResources();
+ for (Object r: BrooklynRestApi.getMiscResources())
+ addResource(r);
+ }
+
+ /** intended for overriding if you only want certain resources added, or additional ones added */
+ protected void addBrooklynResources() {
+ for (Object r: BrooklynRestApi.getBrooklynRestResources())
+ addResource(r);
+ }
+
+ protected void setUpJersey() {
+ setUpResources();
+
+ jerseyTest = createJerseyTest();
+ config = null;
+ try {
+ jerseyTest.setUp();
+ } catch (Exception e) {
+ throw Exceptions.propagate(e);
+ }
+ }
+
+ protected JerseyTest createJerseyTest() {
+ return new JerseyTest() {
+ @Override
+ protected AppDescriptor configure() {
+ return new LowLevelAppDescriptor.Builder(config).build();
+ }
+
+ @Override
+ protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+ return new TestContainerFactory() {
+ TestContainerFactory delegate = new InMemoryTestContainerFactory();
+
+ @Override
+ public Class<? extends AppDescriptor> supports() {
+ return delegate.supports();
+ }
+
+ @Override
+ public TestContainer create(URI baseUri, AppDescriptor ad)
+ throws IllegalArgumentException {
+ URI uri = URI.create(baseUri.toString() + "v1/");
+ System.out.println(uri);;
+ return delegate.create(uri, (LowLevelAppDescriptor)ad);
+ }
+ };
+ }
+ };
+ }
+
+ protected void tearDownJersey() {
+ if (jerseyTest != null) {
+ try {
+ jerseyTest.tearDown();
+ } catch (Exception e) {
+ throw Exceptions.propagate(e);
+ }
+ }
+ config = new DefaultResourceConfig();
+ }
+
+ public Client client() {
+ Preconditions.checkNotNull(jerseyTest, "Must run setUpJersey first");
+ return jerseyTest.client();
+ }
+
+ public WebResource resource(String uri) {
+ Preconditions.checkNotNull(jerseyTest, "Must run setUpJersey first");
+ return jerseyTest.resource().path(uri);
+ }
+}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestResourceTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestResourceTest.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestResourceTest.java
new file mode 100644
index 0000000..b94e73c
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestResourceTest.java
@@ -0,0 +1,154 @@
+/*
+ * 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.testing;
+
+import static org.testng.Assert.assertTrue;
+
+import java.net.URI;
+import java.util.Collection;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+
+import javax.ws.rs.core.MediaType;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.apache.brooklyn.api.entity.Application;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.rest.domain.ApplicationSpec;
+import org.apache.brooklyn.rest.domain.ApplicationSummary;
+import org.apache.brooklyn.rest.domain.Status;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.repeat.Repeater;
+import org.apache.brooklyn.util.time.Duration;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.UniformInterfaceException;
+import com.sun.jersey.spi.inject.Errors;
+
+public abstract class BrooklynRestResourceTest extends BrooklynRestApiTest {
+
+ private static final Logger log = LoggerFactory.getLogger(BrooklynRestResourceTest.class);
+
+ @BeforeClass(alwaysRun = true)
+ public void setUp() throws Exception {
+ // need this to debug jersey inject errors
+ java.util.logging.Logger.getLogger(Errors.class.getName()).setLevel(Level.INFO);
+
+ setUpJersey();
+ }
+
+ @Override
+ @AfterClass(alwaysRun = true)
+ public void tearDown() throws Exception {
+ tearDownJersey();
+ super.tearDown();
+ }
+
+
+ protected ClientResponse clientDeploy(ApplicationSpec spec) {
+ try {
+ // dropwizard TestClient won't skip deserialization of trivial things like string and byte[] and inputstream
+ // if we pass in an object it serializes, so we have to serialize things ourselves
+ return client().resource("/v1/applications")
+ .entity(new ObjectMapper().writer().writeValueAsBytes(spec), MediaType.APPLICATION_OCTET_STREAM)
+ .post(ClientResponse.class);
+ } catch (Exception e) {
+ throw Exceptions.propagate(e);
+ }
+ }
+
+ protected void waitForApplicationToBeRunning(final URI applicationRef) {
+ waitForApplicationToBeRunning(applicationRef, Duration.minutes(3));
+ }
+ protected void waitForApplicationToBeRunning(final URI applicationRef, Duration timeout) {
+ if (applicationRef==null)
+ throw new NullPointerException("No application URI available (consider using BrooklynRestResourceTest.clientDeploy)");
+
+ boolean started = Repeater.create("Wait for application startup")
+ .until(new Callable<Boolean>() {
+ @Override
+ public Boolean call() throws Exception {
+ Status status = getApplicationStatus(applicationRef);
+ if (status == Status.ERROR) {
+ Assert.fail("Application failed with ERROR");
+ }
+ return status == Status.RUNNING;
+ }
+ })
+ .backoffTo(Duration.ONE_SECOND)
+ .limitTimeTo(timeout)
+ .run();
+
+ if (!started) {
+ log.warn("Did not start application "+applicationRef+":");
+ Collection<Application> apps = getManagementContext().getApplications();
+ for (Application app: apps)
+ Entities.dumpInfo(app);
+ }
+ assertTrue(started);
+ }
+
+ protected Status getApplicationStatus(URI uri) {
+ return client().resource(uri).get(ApplicationSummary.class).getStatus();
+ }
+
+ protected void waitForPageFoundResponse(final String resource, final Class<?> clazz) {
+ boolean found = Repeater.create("Wait for page found")
+ .until(new Callable<Boolean>() {
+ @Override
+ public Boolean call() throws Exception {
+ try {
+ client().resource(resource).get(clazz);
+ return true;
+ } catch (UniformInterfaceException e) {
+ return false;
+ }
+ }
+ })
+ .every(1, TimeUnit.SECONDS)
+ .limitTimeTo(30, TimeUnit.SECONDS)
+ .run();
+ assertTrue(found);
+ }
+
+ protected void waitForPageNotFoundResponse(final String resource, final Class<?> clazz) {
+ boolean success = Repeater.create("Wait for page not found")
+ .until(new Callable<Boolean>() {
+ @Override
+ public Boolean call() throws Exception {
+ try {
+ client().resource(resource).get(clazz);
+ return false;
+ } catch (UniformInterfaceException e) {
+ return e.getResponse().getStatus() == 404;
+ }
+ }
+ })
+ .every(1, TimeUnit.SECONDS)
+ .limitTimeTo(30, TimeUnit.SECONDS)
+ .run();
+ assertTrue(success);
+ }
+}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/CapitalizePolicy.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/CapitalizePolicy.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/CapitalizePolicy.java
new file mode 100644
index 0000000..7d80a6f
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/CapitalizePolicy.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.rest.testing.mocks;
+
+import org.apache.brooklyn.api.entity.EntityLocal;
+import org.apache.brooklyn.core.policy.AbstractPolicy;
+
+@SuppressWarnings("deprecation")
+public class CapitalizePolicy extends AbstractPolicy {
+
+ @Override
+ public void setEntity(EntityLocal entity) {
+ super.setEntity(entity);
+ // TODO subscribe to foo and emit an enriched sensor on different channel which is capitalized
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/EverythingGroup.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/EverythingGroup.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/EverythingGroup.java
new file mode 100644
index 0000000..707c4a3
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/EverythingGroup.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.rest.testing.mocks;
+
+import org.apache.brooklyn.api.entity.Group;
+import org.apache.brooklyn.api.entity.ImplementedBy;
+
+@ImplementedBy(EverythingGroupImpl.class)
+public interface EverythingGroup extends Group {
+
+}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/EverythingGroupImpl.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/EverythingGroupImpl.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/EverythingGroupImpl.java
new file mode 100644
index 0000000..8b2c98b
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/EverythingGroupImpl.java
@@ -0,0 +1,32 @@
+/*
+ * 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.testing.mocks;
+
+import org.apache.brooklyn.entity.group.DynamicGroupImpl;
+
+import com.google.common.base.Predicates;
+
+public class EverythingGroupImpl extends DynamicGroupImpl implements EverythingGroup {
+
+ public EverythingGroupImpl() {
+ super();
+ config().set(ENTITY_FILTER, Predicates.alwaysTrue());
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/NameMatcherGroup.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/NameMatcherGroup.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/NameMatcherGroup.java
new file mode 100644
index 0000000..f9a2e21
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/NameMatcherGroup.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.rest.testing.mocks;
+
+import org.apache.brooklyn.api.entity.Group;
+import org.apache.brooklyn.api.entity.ImplementedBy;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
+
+@ImplementedBy(NameMatcherGroupImpl.class)
+public interface NameMatcherGroup extends Group {
+
+ public static final ConfigKey<String> NAME_REGEX = ConfigKeys.newStringConfigKey("namematchergroup.regex");
+}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/NameMatcherGroupImpl.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/NameMatcherGroupImpl.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/NameMatcherGroupImpl.java
new file mode 100644
index 0000000..bec416f
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/NameMatcherGroupImpl.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.rest.testing.mocks;
+
+import org.apache.brooklyn.core.entity.EntityPredicates;
+import org.apache.brooklyn.entity.group.DynamicGroupImpl;
+import org.apache.brooklyn.util.text.StringPredicates;
+
+public class NameMatcherGroupImpl extends DynamicGroupImpl implements NameMatcherGroup {
+
+ @Override
+ public void init() {
+ super.init();
+ config().set(ENTITY_FILTER, EntityPredicates.displayNameSatisfies(StringPredicates.matchesRegex(getConfig(NAME_REGEX))));
+ rescanEntities();
+ }
+}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockApp.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockApp.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockApp.java
new file mode 100644
index 0000000..6d92e65
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockApp.java
@@ -0,0 +1,24 @@
+/*
+ * 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.testing.mocks;
+
+import org.apache.brooklyn.core.entity.AbstractApplication;
+
+public class RestMockApp extends AbstractApplication {
+}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockAppBuilder.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockAppBuilder.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockAppBuilder.java
new file mode 100644
index 0000000..1ca10bd
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockAppBuilder.java
@@ -0,0 +1,39 @@
+/*
+ * 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.testing.mocks;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.core.entity.StartableApplication;
+import org.apache.brooklyn.core.entity.factory.ApplicationBuilder;
+import org.apache.brooklyn.util.javalang.Reflections;
+
+public class RestMockAppBuilder extends ApplicationBuilder {
+
+ public RestMockAppBuilder() {
+ super(EntitySpec.create(StartableApplication.class).impl(RestMockApp.class));
+ }
+
+ @Override
+ protected void doBuild() {
+ addChild(EntitySpec.create(Entity.class).impl(RestMockSimpleEntity.class)
+ .additionalInterfaces(Reflections.getAllInterfaces(RestMockSimpleEntity.class))
+ .displayName("child1"));
+ }
+}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimpleEntity.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimpleEntity.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimpleEntity.java
new file mode 100644
index 0000000..58d24aa
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimpleEntity.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.testing.mocks;
+
+import java.util.Map;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntityLocal;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.annotation.Effector;
+import org.apache.brooklyn.core.annotation.EffectorParam;
+import org.apache.brooklyn.core.config.BasicConfigKey;
+import org.apache.brooklyn.core.effector.MethodEffector;
+import org.apache.brooklyn.core.sensor.BasicAttributeSensor;
+import org.apache.brooklyn.entity.software.base.AbstractSoftwareProcessSshDriver;
+import org.apache.brooklyn.entity.software.base.SoftwareProcessImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+import org.apache.brooklyn.util.core.flags.SetFromFlag;
+
+public class RestMockSimpleEntity extends SoftwareProcessImpl {
+
+ private static final Logger log = LoggerFactory.getLogger(RestMockSimpleEntity.class);
+
+ public RestMockSimpleEntity() {
+ super();
+ }
+
+ public RestMockSimpleEntity(Entity parent) {
+ super(parent);
+ }
+
+ public RestMockSimpleEntity(@SuppressWarnings("rawtypes") Map flags, Entity parent) {
+ super(flags, parent);
+ }
+
+ public RestMockSimpleEntity(@SuppressWarnings("rawtypes") Map flags) {
+ super(flags);
+ }
+
+ @Override
+ protected void connectSensors() {
+ super.connectSensors();
+ connectServiceUpIsRunning();
+ }
+
+ @SetFromFlag("sampleConfig")
+ public static final ConfigKey<String> SAMPLE_CONFIG = new BasicConfigKey<String>(
+ String.class, "brooklyn.rest.mock.sample.config", "Mock sample config", "DEFAULT_VALUE");
+
+ public static final AttributeSensor<String> SAMPLE_SENSOR = new BasicAttributeSensor<String>(
+ String.class, "brooklyn.rest.mock.sample.sensor", "Mock sample sensor");
+
+ public static final MethodEffector<String> SAMPLE_EFFECTOR = new MethodEffector<String>(RestMockSimpleEntity.class, "sampleEffector");
+
+ @Effector
+ public String sampleEffector(@EffectorParam(name="param1", description="param one") String param1,
+ @EffectorParam(name="param2") Integer param2) {
+ log.info("Invoked sampleEffector("+param1+","+param2+")");
+ String result = ""+param1+param2;
+ sensors().set(SAMPLE_SENSOR, result);
+ return result;
+ }
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ public Class getDriverInterface() {
+ return MockSshDriver.class;
+ }
+
+ public static class MockSshDriver extends AbstractSoftwareProcessSshDriver {
+ public MockSshDriver(EntityLocal entity, SshMachineLocation machine) {
+ super(entity, machine);
+ }
+ public boolean isRunning() { return true; }
+ public void stop() {}
+ public void kill() {}
+ public void install() {}
+ public void customize() {}
+ public void launch() {}
+ public void setup() { }
+ public void copyInstallResources() { }
+ public void copyRuntimeResources() { }
+ }
+}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimplePolicy.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimplePolicy.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimplePolicy.java
new file mode 100644
index 0000000..e15cdd1
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimplePolicy.java
@@ -0,0 +1,64 @@
+/*
+ * 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.testing.mocks;
+
+import java.util.Map;
+
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.BasicConfigKey;
+import org.apache.brooklyn.core.policy.AbstractPolicy;
+import org.apache.brooklyn.util.core.flags.SetFromFlag;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class RestMockSimplePolicy extends AbstractPolicy {
+
+ @SuppressWarnings("unused")
+ private static final Logger log = LoggerFactory.getLogger(RestMockSimplePolicy.class);
+
+ public RestMockSimplePolicy() {
+ super();
+ }
+
+ @SuppressWarnings("rawtypes")
+ public RestMockSimplePolicy(Map flags) {
+ super(flags);
+ }
+
+ @SetFromFlag("sampleConfig")
+ public static final ConfigKey<String> SAMPLE_CONFIG = BasicConfigKey.builder(String.class)
+ .name("brooklyn.rest.mock.sample.config")
+ .description("Mock sample config")
+ .defaultValue("DEFAULT_VALUE")
+ .reconfigurable(true)
+ .build();
+
+ @SetFromFlag
+ public static final ConfigKey<Integer> INTEGER_CONFIG = BasicConfigKey.builder(Integer.class)
+ .name("brooklyn.rest.mock.sample.integer")
+ .description("Mock integer config")
+ .defaultValue(1)
+ .reconfigurable(true)
+ .build();
+
+ @Override
+ protected <T> void doReconfigureConfig(ConfigKey<T> key, T val) {
+ // no-op
+ }
+}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/abd2d5f3/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtilsTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtilsTest.java b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtilsTest.java
new file mode 100644
index 0000000..48908e3
--- /dev/null
+++ b/rest/rest-server-jersey/src/test/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtilsTest.java
@@ -0,0 +1,213 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.rest.util;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Map;
+
+import org.apache.brooklyn.api.catalog.Catalog;
+import org.apache.brooklyn.api.entity.Application;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.policy.Policy;
+import org.apache.brooklyn.core.catalog.internal.CatalogItemBuilder;
+import org.apache.brooklyn.core.catalog.internal.CatalogTemplateItemDto;
+import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
+import org.apache.brooklyn.core.entity.AbstractApplication;
+import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
+import org.apache.brooklyn.core.objs.proxy.EntityProxy;
+import org.apache.brooklyn.core.policy.AbstractPolicy;
+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.entity.stock.BasicEntity;
+import org.apache.brooklyn.rest.domain.ApplicationSpec;
+import org.apache.brooklyn.rest.domain.EntitySpec;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+
+public class BrooklynRestResourceUtilsTest {
+
+ private LocalManagementContext managementContext;
+ private BrooklynRestResourceUtils util;
+
+ @BeforeMethod(alwaysRun=true)
+ public void setUp() throws Exception {
+ managementContext = LocalManagementContextForTests.newInstance();
+ util = new BrooklynRestResourceUtils(managementContext);
+ }
+
+ @AfterMethod(alwaysRun=true)
+ public void tearDown() throws Exception {
+ if (managementContext != null) managementContext.terminate();
+ }
+
+ @Test
+ public void testCreateAppFromImplClass() {
+ ApplicationSpec spec = ApplicationSpec.builder()
+ .name("myname")
+ .type(SampleNoOpApplication.class.getName())
+ .locations(ImmutableSet.of("localhost"))
+ .build();
+ Application app = util.create(spec);
+
+ assertEquals(ImmutableList.copyOf(managementContext.getApplications()), ImmutableList.of(app));
+ assertEquals(app.getDisplayName(), "myname");
+ assertTrue(app instanceof EntityProxy);
+ assertTrue(app instanceof MyInterface);
+ assertFalse(app instanceof SampleNoOpApplication);
+ }
+
+ @Test
+ public void testCreateAppFromCatalogByType() {
+ createAppFromCatalog(SampleNoOpApplication.class.getName());
+ }
+
+ @Test
+ public void testCreateAppFromCatalogByName() {
+ createAppFromCatalog("app.noop");
+ }
+
+ @Test
+ public void testCreateAppFromCatalogById() {
+ createAppFromCatalog("app.noop:0.0.1");
+ }
+
+ @Test
+ @SuppressWarnings("deprecation")
+ public void testCreateAppFromCatalogByTypeMultipleItems() {
+ CatalogTemplateItemDto item = CatalogItemBuilder.newTemplate("app.noop", "0.0.2-SNAPSHOT")
+ .javaType(SampleNoOpApplication.class.getName())
+ .build();
+ managementContext.getCatalog().addItem(item);
+ createAppFromCatalog(SampleNoOpApplication.class.getName());
+ }
+
+ @SuppressWarnings("deprecation")
+ private void createAppFromCatalog(String type) {
+ CatalogTemplateItemDto item = CatalogItemBuilder.newTemplate("app.noop", "0.0.1")
+ .javaType(SampleNoOpApplication.class.getName())
+ .build();
+ managementContext.getCatalog().addItem(item);
+
+ ApplicationSpec spec = ApplicationSpec.builder()
+ .name("myname")
+ .type(type)
+ .locations(ImmutableSet.of("localhost"))
+ .build();
+ Application app = util.create(spec);
+
+ assertEquals(app.getCatalogItemId(), "app.noop:0.0.1");
+ }
+
+ @Test
+ public void testEntityAppFromCatalogByType() {
+ createEntityFromCatalog(BasicEntity.class.getName());
+ }
+
+ @Test
+ public void testEntityAppFromCatalogByName() {
+ createEntityFromCatalog("app.basic");
+ }
+
+ @Test
+ public void testEntityAppFromCatalogById() {
+ createEntityFromCatalog("app.basic:0.0.1");
+ }
+
+ @Test
+ @SuppressWarnings("deprecation")
+ public void testEntityAppFromCatalogByTypeMultipleItems() {
+ CatalogTemplateItemDto item = CatalogItemBuilder.newTemplate("app.basic", "0.0.2-SNAPSHOT")
+ .javaType(SampleNoOpApplication.class.getName())
+ .build();
+ managementContext.getCatalog().addItem(item);
+ createEntityFromCatalog(BasicEntity.class.getName());
+ }
+
+ @SuppressWarnings("deprecation")
+ private void createEntityFromCatalog(String type) {
+ String symbolicName = "app.basic";
+ String version = "0.0.1";
+ CatalogTemplateItemDto item = CatalogItemBuilder.newTemplate(symbolicName, version)
+ .javaType(BasicEntity.class.getName())
+ .build();
+ managementContext.getCatalog().addItem(item);
+
+ ApplicationSpec spec = ApplicationSpec.builder()
+ .name("myname")
+ .entities(ImmutableSet.of(new EntitySpec(type)))
+ .locations(ImmutableSet.of("localhost"))
+ .build();
+ Application app = util.create(spec);
+
+ Entity entity = Iterables.getOnlyElement(app.getChildren());
+ assertEquals(entity.getCatalogItemId(), CatalogUtils.getVersionedId(symbolicName, version));
+ }
+
+ @Test
+ public void testNestedApplications() {
+ // hierarchy is: app -> subapp -> subentity (where subentity has a policy)
+
+ Application app = managementContext.getEntityManager().createEntity(org.apache.brooklyn.api.entity.EntitySpec.create(TestApplication.class)
+ .displayName("app")
+ .child(org.apache.brooklyn.api.entity.EntitySpec.create(TestApplication.class)
+ .displayName("subapp")
+ .child(org.apache.brooklyn.api.entity.EntitySpec.create(TestEntity.class)
+ .displayName("subentity")
+ .policy(org.apache.brooklyn.api.policy.PolicySpec.create(MyPolicy.class)
+ .displayName("mypolicy")))));
+
+ Application subapp = (Application) Iterables.getOnlyElement(app.getChildren());
+ TestEntity subentity = (TestEntity) Iterables.getOnlyElement(subapp.getChildren());
+
+ Entity subappRetrieved = util.getEntity(app.getId(), subapp.getId());
+ assertEquals(subappRetrieved.getDisplayName(), "subapp");
+
+ Entity subentityRetrieved = util.getEntity(app.getId(), subentity.getId());
+ assertEquals(subentityRetrieved.getDisplayName(), "subentity");
+
+ Policy subappPolicy = util.getPolicy(app.getId(), subentity.getId(), "mypolicy");
+ assertEquals(subappPolicy.getDisplayName(), "mypolicy");
+ }
+
+ public interface MyInterface {
+ }
+
+ @Catalog(name="Sample No-Op Application",
+ description="Application which does nothing, included only as part of the test cases.",
+ iconUrl="")
+ public static class SampleNoOpApplication extends AbstractApplication implements MyInterface {
+ }
+
+ public static class MyPolicy extends AbstractPolicy {
+ public MyPolicy() {
+ }
+ public MyPolicy(Map<String, ?> flags) {
+ super(flags);
+ }
+ }
+}