Repository: brooklyn-server
Updated Branches:
refs/heads/master 2bad5261a -> 092edf1eb
Adds `PUT /applications/{appid}`
For deploying an app with a pre-defined app-id.
This feature is marked as beta.
Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/3cd14917
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/3cd14917
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/3cd14917
Branch: refs/heads/master
Commit: 3cd14917cfbb83c90084b50086fe5b8e61ad5cc8
Parents: d73a91d
Author: Aled Sage <[email protected]>
Authored: Tue Jul 25 17:25:31 2017 +0100
Committer: Aled Sage <[email protected]>
Committed: Fri Jul 28 10:24:28 2017 +0100
----------------------------------------------------------------------
.../core/mgmt/EntityManagementUtils.java | 13 ++-
.../mgmt/internal/EntityManagerInternal.java | 10 ++
.../mgmt/internal/IdAlreadyExistsException.java | 36 +++++++
.../core/mgmt/internal/LocalEntityManager.java | 34 ++++--
.../internal/NonDeploymentEntityManager.java | 10 ++
.../core/objs/proxy/InternalEntityFactory.java | 28 ++---
.../core/entity/proxying/EntityManagerTest.java | 23 ++++
.../proxying/InternalEntityFactoryTest.java | 17 ++-
.../mgmt/osgi/OsgiVersionMoreEntityTest.java | 3 +-
.../brooklyn/rest/api/ApplicationApi.java | 28 ++++-
.../rest/resources/ApplicationResource.java | 39 +++++--
.../rest/resources/ApplicationResourceTest.java | 105 +++++++++++++++++++
.../rest/testing/BrooklynRestResourceTest.java | 11 ++
13 files changed, 319 insertions(+), 38 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/3cd14917/core/src/main/java/org/apache/brooklyn/core/mgmt/EntityManagementUtils.java
----------------------------------------------------------------------
diff --git
a/core/src/main/java/org/apache/brooklyn/core/mgmt/EntityManagementUtils.java
b/core/src/main/java/org/apache/brooklyn/core/mgmt/EntityManagementUtils.java
index 8fb6345..a036622 100644
---
a/core/src/main/java/org/apache/brooklyn/core/mgmt/EntityManagementUtils.java
+++
b/core/src/main/java/org/apache/brooklyn/core/mgmt/EntityManagementUtils.java
@@ -40,6 +40,7 @@ import org.apache.brooklyn.core.effector.Effectors;
import org.apache.brooklyn.core.entity.Entities;
import org.apache.brooklyn.core.entity.EntityFunctions;
import org.apache.brooklyn.core.entity.trait.Startable;
+import org.apache.brooklyn.core.mgmt.internal.EntityManagerInternal;
import org.apache.brooklyn.core.typereg.RegisteredTypeLoadingContexts;
import org.apache.brooklyn.entity.stock.BasicApplication;
import org.apache.brooklyn.util.collections.MutableList;
@@ -51,6 +52,8 @@ import org.apache.brooklyn.util.time.Duration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -80,7 +83,15 @@ public class EntityManagementUtils {
/** creates an application from the given app spec, managed by the given
management context */
public static <T extends Application> T createUnstarted(ManagementContext
mgmt, EntitySpec<T> spec) {
- T app = mgmt.getEntityManager().createEntity(spec);
+ return createUnstarted(mgmt, spec, Optional.absent());
+ }
+
+ /**
+ * As {@link #createUnstarted(ManagementContext, EntitySpec)}, but uses
the given entity id (if present).
+ */
+ @Beta
+ public static <T extends Application> T createUnstarted(ManagementContext
mgmt, EntitySpec<T> spec, Optional<String> entityId) {
+ T app =
((EntityManagerInternal)mgmt.getEntityManager()).createEntity(spec, entityId);
return app;
}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/3cd14917/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/EntityManagerInternal.java
----------------------------------------------------------------------
diff --git
a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/EntityManagerInternal.java
b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/EntityManagerInternal.java
index 7bad213..9d0ab49 100644
---
a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/EntityManagerInternal.java
+++
b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/EntityManagerInternal.java
@@ -20,8 +20,12 @@ package org.apache.brooklyn.core.mgmt.internal;
import org.apache.brooklyn.api.entity.Application;
import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntitySpec;
import org.apache.brooklyn.api.mgmt.EntityManager;
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
+
public interface EntityManagerInternal extends EntityManager,
BrooklynObjectManagerInternal<Entity> {
/** gets all entities currently known to the application, including
entities that are not yet managed */
@@ -29,4 +33,10 @@ public interface EntityManagerInternal extends
EntityManager, BrooklynObjectMana
public Iterable<String> getEntityIds();
+ /**
+ * Same as {@link #createEntity(EntitySpec)}, but takes an optional entity
id that will be
+ * used for the entity.
+ */
+ @Beta
+ <T extends Entity> T createEntity(EntitySpec<T> spec, Optional<String>
entityId);
}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/3cd14917/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/IdAlreadyExistsException.java
----------------------------------------------------------------------
diff --git
a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/IdAlreadyExistsException.java
b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/IdAlreadyExistsException.java
new file mode 100644
index 0000000..b2c23bd
--- /dev/null
+++
b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/IdAlreadyExistsException.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.core.mgmt.internal;
+
+/**
+ * Indicates that there is an id conflict (e.g. when creating an app using a
pre-defined id,
+ * using {@code PUT /applications/abcdefghijklm}).
+ */
+public class IdAlreadyExistsException extends RuntimeException {
+
+ private static final long serialVersionUID = -602477310528752776L;
+
+ public IdAlreadyExistsException(String msg) {
+ super(msg);
+ }
+
+ public IdAlreadyExistsException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/3cd14917/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalEntityManager.java
----------------------------------------------------------------------
diff --git
a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalEntityManager.java
b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalEntityManager.java
index 155286b..8d5ddf6 100644
---
a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalEntityManager.java
+++
b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalEntityManager.java
@@ -69,6 +69,7 @@ import org.slf4j.LoggerFactory;
import com.google.common.annotations.Beta;
import com.google.common.base.Function;
+import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
@@ -143,12 +144,18 @@ public class LocalEntityManager implements
EntityManagerInternal {
if (!isRunning()) throw new IllegalStateException("Management context
no longer running");
return entityTypeRegistry;
}
+
+ @Override
+ public <T extends Entity> T createEntity(EntitySpec<T> spec) {
+ return createEntity(spec, Optional.absent());
+ }
+ @Beta
@SuppressWarnings("unchecked")
@Override
- public <T extends Entity> T createEntity(EntitySpec<T> spec) {
+ public <T extends Entity> T createEntity(EntitySpec<T> spec,
Optional<String> entityId) {
try {
- T entity = entityFactory.createEntity(spec);
+ T entity = entityFactory.createEntity(spec, entityId);
Entity proxy = ((AbstractEntity)entity).getProxy();
checkNotNull(proxy, "proxy for entity %s, spec %s", entity, spec);
@@ -263,7 +270,9 @@ public class LocalEntityManager implements
EntityManagerInternal {
@Override
public boolean isManaged(Entity e) {
- return (isRunning() && getEntity(e.getId()) != null);
+ // Confirm we know about this entity (by id), and that it is the same
entity instance
+ // (rather than just a different unmanaged entity with the same id).
+ return (isRunning() && getEntity(e.getId()) != null) &&
(entitiesById.get(e.getId()) == deproxyIfNecessary(e));
}
boolean isPreRegistered(Entity e) {
@@ -670,23 +679,26 @@ public class LocalEntityManager implements
EntityManagerInternal {
Entity old = entitiesById.get(e.getId());
if (old!=null && mode.wasNotLoaded()) {
- if (old.equals(e)) {
+ if (old == deproxyIfNecessary(e)) {
log.warn("{} redundant call to start management of entity {};
ignoring", this, e);
} else {
- throw new IllegalStateException("call to manage entity "+e+"
("+mode+") but different entity "+old+" already known under that id at "+this);
+ throw new IdAlreadyExistsException("call to manage entity
"+e+" ("+mode+") but "
+ + "different entity "+old+" already known under that
id '"+e.getId()+"' at "+this);
}
return false;
}
BrooklynLogging.log(log,
BrooklynLogging.levelDebugOrTraceIfReadOnly(e),
- "{} starting management of entity {}", this, e);
+ "{} starting management of entity {}", this, e);
Entity realE = toRealEntity(e);
Entity oldProxy = entityProxiesById.get(e.getId());
Entity proxyE;
if (oldProxy!=null) {
if (mode.wasNotLoaded()) {
- throw new IllegalStateException("call to manage entity "+e+"
from unloaded state ("+mode+") but already had proxy "+oldProxy+" already known
under that id at "+this);
+ throw new IdAlreadyExistsException("call to manage entity
"+e+" from unloaded "
+ + "state ("+mode+") but already had proxy "+oldProxy+"
already known "
+ + "under that id '"+e.getId()+"' at "+this);
}
// make the old proxy point at this new delegate
// (some other tricks done in the call below)
@@ -698,7 +710,7 @@ public class LocalEntityManager implements
EntityManagerInternal {
entityProxiesById.put(e.getId(), proxyE);
entityTypes.put(e.getId(), realE.getClass().getName());
entitiesById.put(e.getId(), realE);
-
+
preManagedEntitiesById.remove(e.getId());
if ((e instanceof Application) && (e.getParent()==null)) {
applications.add((Application)proxyE);
@@ -764,6 +776,7 @@ public class LocalEntityManager implements
EntityManagerInternal {
entities.remove(proxyE);
entityProxiesById.remove(e.getId());
entityModesById.remove(e.getId());
+
Object old = entitiesById.remove(e.getId());
entityTypes.remove(e.getId());
@@ -868,6 +881,11 @@ public class LocalEntityManager implements
EntityManagerInternal {
return result;
}
+ private Entity deproxyIfNecessary(Entity e) {
+ return (e instanceof AbstractEntity) ? e : Entities.deproxy(e);
+ }
+
+
private boolean isRunning() {
return managementContext.isRunning();
}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/3cd14917/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/NonDeploymentEntityManager.java
----------------------------------------------------------------------
diff --git
a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/NonDeploymentEntityManager.java
b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/NonDeploymentEntityManager.java
index a980a3c..8289ab7 100644
---
a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/NonDeploymentEntityManager.java
+++
b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/NonDeploymentEntityManager.java
@@ -32,6 +32,7 @@ import org.apache.brooklyn.api.policy.PolicySpec;
import org.apache.brooklyn.api.sensor.Enricher;
import org.apache.brooklyn.api.sensor.EnricherSpec;
+import com.google.common.base.Optional;
import com.google.common.base.Predicate;
public class NonDeploymentEntityManager implements EntityManagerInternal {
@@ -61,6 +62,15 @@ public class NonDeploymentEntityManager implements
EntityManagerInternal {
}
@Override
+ public <T extends Entity> T createEntity(EntitySpec<T> spec,
Optional<String> entityId) {
+ if (isInitialManagementContextReal()) {
+ return
((EntityManagerInternal)initialManagementContext.getEntityManager()).createEntity(spec,
entityId);
+ } else {
+ throw new IllegalStateException("Non-deployment context "+this+"
(with no initial management context supplied) is not valid for this
operation.");
+ }
+ }
+
+ @Override
public <T extends Entity> T createEntity(Map<?,?> config, Class<T> type) {
return createEntity(EntitySpec.create(type).configure(config));
}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/3cd14917/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalEntityFactory.java
----------------------------------------------------------------------
diff --git
a/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalEntityFactory.java
b/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalEntityFactory.java
index 0cf933c..e3ca216 100644
---
a/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalEntityFactory.java
+++
b/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalEntityFactory.java
@@ -63,6 +63,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
@@ -171,7 +172,7 @@ public class InternalEntityFactory extends InternalFactory {
* {@link Entities#manage(Entity)} (if it's in a managed application)
* or {@link
Entities#startManagement(org.apache.brooklyn.api.entity.Application,
org.apache.brooklyn.api.management.ManagementContext)}
* (if it's an application) */
- public <T extends Entity> T createEntity(EntitySpec<T> spec) {
+ public <T extends Entity> T createEntity(EntitySpec<T> spec,
Optional<String> entityId) {
/* Order is important here. Changed Jul 2014 when supporting children
in spec.
* (Previously was much simpler, and parent was set right after
running initializers; and there were no children.)
* <p>
@@ -186,12 +187,13 @@ public class InternalEntityFactory extends
InternalFactory {
Map<String,Entity> entitiesByEntityId = MutableMap.of();
Map<String,EntitySpec<?>> specsByEntityId = MutableMap.of();
- T entity = createEntityAndDescendantsUninitialized(spec,
entitiesByEntityId, specsByEntityId);
+ T entity = createEntityAndDescendantsUninitialized(spec, entityId,
entitiesByEntityId, specsByEntityId);
initEntityAndDescendants(entity.getId(), entitiesByEntityId,
specsByEntityId);
return entity;
}
- protected <T extends Entity> T
createEntityAndDescendantsUninitialized(EntitySpec<T> spec, Map<String,Entity>
entitiesByEntityId, Map<String,EntitySpec<?>> specsByEntityId) {
+ private <T extends Entity> T
createEntityAndDescendantsUninitialized(EntitySpec<T> spec, Optional<String>
entityId,
+ Map<String,Entity> entitiesByEntityId, Map<String,EntitySpec<?>>
specsByEntityId) {
if (spec.getFlags().containsKey("parent") ||
spec.getFlags().containsKey("owner")) {
throw new IllegalArgumentException("Spec's flags must not contain
parent or owner; use spec.parent() instead for "+spec);
}
@@ -202,7 +204,7 @@ public class InternalEntityFactory extends InternalFactory {
try {
Class<? extends T> clazz = getImplementedBy(spec);
- T entity = constructEntity(clazz, spec);
+ T entity = constructEntity(clazz, spec, entityId);
loadUnitializedEntity(entity, spec);
@@ -218,7 +220,7 @@ public class InternalEntityFactory extends InternalFactory {
log.warn("Child spec "+childSpec+" is already set with
parent "+entity+"; how did this happen?!");
}
childSpec.parent(entity);
- Entity child =
createEntityAndDescendantsUninitialized(childSpec, entitiesByEntityId,
specsByEntityId);
+ Entity child =
createEntityAndDescendantsUninitialized(childSpec, Optional.absent(),
entitiesByEntityId, specsByEntityId);
entity.addChild(child);
}
@@ -392,10 +394,9 @@ public class InternalEntityFactory extends InternalFactory
{
* configuration from the {@link EntitySpec} is <b>not</b> normally
applied,
* although for old-style entities flags from the spec are passed to the
constructor.
* <p>
- * @deprecated since 0.9.0 becoming private
- */ @Deprecated
- public <T extends Entity> T constructEntity(Class<? extends T> clazz,
EntitySpec<T> spec) {
- T entity = constructEntityImpl(clazz, spec, null, null);
+ */
+ private <T extends Entity> T constructEntity(Class<? extends T> clazz,
EntitySpec<T> spec, Optional<String> entityId) {
+ T entity = constructEntityImpl(clazz, spec, null, entityId);
if (((AbstractEntity)entity).getProxy() == null)
((AbstractEntity)entity).setProxy(createEntityProxy(spec, entity));
return entity;
}
@@ -414,7 +415,7 @@ public class InternalEntityFactory extends InternalFactory {
checkNotNull(entityId, "entityId");
checkState(interfaces != null && !Iterables.isEmpty(interfaces), "must
have at least one interface for entity %s:%s", clazz, entityId);
- T entity = constructEntityImpl(clazz, null, null, entityId);
+ T entity = constructEntityImpl(clazz, null, null,
Optional.of(entityId));
if (((AbstractEntity)entity).getProxy() == null) {
Entity proxy =
managementContext.getEntityManager().getEntity(entity.getId());
if (proxy==null) {
@@ -429,11 +430,12 @@ public class InternalEntityFactory extends
InternalFactory {
return entity;
}
- private <T extends Entity> T constructEntityImpl(Class<? extends T> clazz,
EntitySpec<?> optionalSpec, Map<String, ?> optionalConstructorFlags, String
optionalEntityId) {
+ private <T extends Entity> T constructEntityImpl(Class<? extends T> clazz,
EntitySpec<?> optionalSpec,
+ Map<String, ?> optionalConstructorFlags, Optional<String>
entityId) {
T entity = construct(clazz, optionalSpec, optionalConstructorFlags);
- if (optionalEntityId != null) {
- FlagUtils.setFieldsFromFlags(ImmutableMap.of("id",
optionalEntityId), entity);
+ if (entityId.isPresent()) {
+ FlagUtils.setFieldsFromFlags(ImmutableMap.of("id",
entityId.get()), entity);
}
if (entity instanceof AbstractApplication) {
FlagUtils.setFieldsFromFlags(ImmutableMap.of("mgmt",
managementContext), entity);
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/3cd14917/core/src/test/java/org/apache/brooklyn/core/entity/proxying/EntityManagerTest.java
----------------------------------------------------------------------
diff --git
a/core/src/test/java/org/apache/brooklyn/core/entity/proxying/EntityManagerTest.java
b/core/src/test/java/org/apache/brooklyn/core/entity/proxying/EntityManagerTest.java
index 976beda..7d21071 100644
---
a/core/src/test/java/org/apache/brooklyn/core/entity/proxying/EntityManagerTest.java
+++
b/core/src/test/java/org/apache/brooklyn/core/entity/proxying/EntityManagerTest.java
@@ -20,6 +20,7 @@ package org.apache.brooklyn.core.entity.proxying;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertSame;
import static org.testng.Assert.assertTrue;
import java.util.ConcurrentModificationException;
@@ -30,9 +31,12 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
+import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.api.entity.EntitySpec;
import org.apache.brooklyn.api.mgmt.EntityManager;
import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.mgmt.internal.EntityManagerInternal;
+import org.apache.brooklyn.core.mgmt.internal.IdAlreadyExistsException;
import org.apache.brooklyn.core.mgmt.internal.LocalEntityManager;
import org.apache.brooklyn.core.objs.proxy.EntityProxy;
import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
@@ -47,6 +51,7 @@ import org.slf4j.LoggerFactory;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
+import com.google.common.base.Optional;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
@@ -114,6 +119,24 @@ public class EntityManagerTest extends
BrooklynAppUnitTestSupport {
Asserts.assertEqualsIgnoringOrder(entityManager.findEntitiesInApplication(app,
Predicates.instanceOf(TestApplication.class)), ImmutableList.of(app));
}
+ @Test
+ public void testCreateEntitiesWithDuplicateIdFails() {
+ TestApplication origApp = app;
+ Entity origDeproxiedApp = Entities.deproxy(app);
+
+ try {
+ TestApplication app2 =
((EntityManagerInternal)entityManager).createEntity(EntitySpec.create(TestApplication.class),
Optional.of(app.getId()));
+ Asserts.shouldHaveFailedPreviously("app2="+app2);
+ } catch (IdAlreadyExistsException e) {
+ // success
+ }
+
+ // Should not have affected the existing app!
+ Entity postApp = entityManager.getEntity(app.getId());
+ assertSame(postApp, origApp);
+ assertSame(Entities.deproxy(postApp), origDeproxiedApp);
+ }
+
// See https://issues.apache.org/jira/browse/BROOKLYN-352
// Before the fix, 250ms was sufficient to cause the
ConcurrentModificationException
@Test
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/3cd14917/core/src/test/java/org/apache/brooklyn/core/entity/proxying/InternalEntityFactoryTest.java
----------------------------------------------------------------------
diff --git
a/core/src/test/java/org/apache/brooklyn/core/entity/proxying/InternalEntityFactoryTest.java
b/core/src/test/java/org/apache/brooklyn/core/entity/proxying/InternalEntityFactoryTest.java
index 7439b71..816242a 100644
---
a/core/src/test/java/org/apache/brooklyn/core/entity/proxying/InternalEntityFactoryTest.java
+++
b/core/src/test/java/org/apache/brooklyn/core/entity/proxying/InternalEntityFactoryTest.java
@@ -41,6 +41,8 @@ import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
+import com.google.common.base.Optional;
+
public class InternalEntityFactoryTest {
private ManagementContextInternal mgmt;
@@ -61,7 +63,7 @@ public class InternalEntityFactoryTest {
@Test
public void testCreatesEntity() throws Exception {
EntitySpec<TestApplication> spec =
EntitySpec.create(TestApplication.class);
- TestApplicationImpl app = (TestApplicationImpl)
factory.createEntity(spec);
+ TestApplicationImpl app = (TestApplicationImpl)
factory.createEntity(spec, Optional.absent());
Entity proxy = app.getProxy();
assertTrue(proxy instanceof Application, "proxy="+app);
@@ -74,7 +76,7 @@ public class InternalEntityFactoryTest {
@Test
public void testCreatesProxy() throws Exception {
EntitySpec<Application> spec =
EntitySpec.create(Application.class).impl(TestApplicationImpl.class);
- Application app = factory.createEntity(spec);
+ Application app = factory.createEntity(spec, Optional.absent());
Application proxy = factory.createEntityProxy(spec, app);
TestApplicationImpl deproxied = (TestApplicationImpl)
Entities.deproxy(proxy);
@@ -89,18 +91,25 @@ public class InternalEntityFactoryTest {
}
@Test
+ public void testSetsEntityId() throws Exception {
+ EntitySpec<TestApplication> spec =
EntitySpec.create(TestApplication.class);
+ TestApplication app = factory.createEntity(spec,
Optional.of("myentityid"));
+ assertEquals(app.getId(), "myentityid");
+ }
+
+ @Test
public void testSetsEntityIsLegacyConstruction() throws Exception {
TestEntity legacy = new TestEntityImpl();
assertTrue(legacy.isLegacyConstruction());
- TestEntity entity =
factory.createEntity(EntitySpec.create(TestEntity.class));
+ TestEntity entity =
factory.createEntity(EntitySpec.create(TestEntity.class), Optional.absent());
assertFalse(entity.isLegacyConstruction());
}
@Test
public void testCreatesProxyImplementingAdditionalInterfaces() throws
Exception {
EntitySpec<Application> spec =
EntitySpec.create(Application.class).impl(MyApplicationImpl.class).additionalInterfaces(MyInterface.class);
- Application app = factory.createEntity(spec);
+ Application app = factory.createEntity(spec, Optional.absent());
Application proxy = factory.createEntityProxy(spec, app);
assertFalse(proxy instanceof MyApplicationImpl, "proxy="+proxy);
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/3cd14917/core/src/test/java/org/apache/brooklyn/core/mgmt/osgi/OsgiVersionMoreEntityTest.java
----------------------------------------------------------------------
diff --git
a/core/src/test/java/org/apache/brooklyn/core/mgmt/osgi/OsgiVersionMoreEntityTest.java
b/core/src/test/java/org/apache/brooklyn/core/mgmt/osgi/OsgiVersionMoreEntityTest.java
index 0e19f52..76002a4 100644
---
a/core/src/test/java/org/apache/brooklyn/core/mgmt/osgi/OsgiVersionMoreEntityTest.java
+++
b/core/src/test/java/org/apache/brooklyn/core/mgmt/osgi/OsgiVersionMoreEntityTest.java
@@ -62,6 +62,7 @@ import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
+import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
@@ -117,7 +118,7 @@ public class OsgiVersionMoreEntityTest implements
OsgiTestResources {
@SuppressWarnings("unchecked")
EntitySpec<Entity> spec =
(((EntitySpec<Entity>)EntitySpec.create(bundleInterface))).impl(bundleCls);
- AbstractEntity entityImpl = (AbstractEntity)
factory.createEntity(spec);
+ AbstractEntity entityImpl = (AbstractEntity)
factory.createEntity(spec, Optional.absent());
Entity entityProxy = factory.createEntityProxy(spec, entityImpl);
assertTrue(entityProxy instanceof EntityProxy,
"proxy="+entityProxy);
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/3cd14917/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/ApplicationApi.java
----------------------------------------------------------------------
diff --git
a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/ApplicationApi.java
b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/ApplicationApi.java
index d0e4d19..2e66fa4 100644
---
a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/ApplicationApi.java
+++
b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/ApplicationApi.java
@@ -27,6 +27,7 @@ import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
@@ -38,6 +39,8 @@ import org.apache.brooklyn.rest.domain.ApplicationSummary;
import org.apache.brooklyn.rest.domain.EntityDetail;
import org.apache.brooklyn.rest.domain.EntitySummary;
+import com.google.common.annotations.Beta;
+
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
@@ -101,7 +104,6 @@ public interface ApplicationApi {
)
@ApiResponses(value = {
@ApiResponse(code = 404, message = "Undefined entity or location"),
- @ApiResponse(code = 412, message = "Application already
registered")
})
public Response createFromYaml(
@ApiParam(
@@ -110,6 +112,24 @@ public interface ApplicationApi {
required = true)
String yaml);
+ @Beta
+ @PUT
+ @Path("/{application}")
+ @Consumes({"application/x-yaml",
+ // see http://stackoverflow.com/questions/332129/yaml-mime-type
+ "text/yaml", "text/x-yaml", "application/yaml"})
+ @ApiOperation(
+ value = "[BETA] Create and start a new application from YAML, with
the given id",
+ response = org.apache.brooklyn.rest.domain.TaskSummary.class
+ )
+ @ApiResponses(value = {
+ @ApiResponse(code = 404, message = "Undefined entity or location"),
+ @ApiResponse(code = 409, message = "Application already
registered")
+ })
+ public Response createFromYamlWithAppId(
+ @ApiParam(name = "applicationSpec", value = "App spec in CAMP YAML
format", required = true) String yaml,
+ @ApiParam(name = "application", value = "Application id", required
= true) @PathParam("application") String appId);
+
@POST
@Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_OCTET_STREAM,
MediaType.TEXT_PLAIN})
@ApiOperation(
@@ -117,8 +137,7 @@ public interface ApplicationApi {
response = org.apache.brooklyn.rest.domain.TaskSummary.class
)
@ApiResponses(value = {
- @ApiResponse(code = 404, message = "Undefined entity or location"),
- @ApiResponse(code = 412, message = "Application already
registered")
+ @ApiResponse(code = 404, message = "Undefined entity or location")
})
public Response createPoly(
@ApiParam(
@@ -134,8 +153,7 @@ public interface ApplicationApi {
response = org.apache.brooklyn.rest.domain.TaskSummary.class
)
@ApiResponses(value = {
- @ApiResponse(code = 404, message = "Undefined entity or location"),
- @ApiResponse(code = 412, message = "Application already
registered")
+ @ApiResponse(code = 404, message = "Undefined entity or location")
})
public Response createFromForm(
@ApiParam(
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/3cd14917/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java
----------------------------------------------------------------------
diff --git
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java
b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java
index 1a42103..973eebe 100644
---
a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java
+++
b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java
@@ -19,9 +19,9 @@
package org.apache.brooklyn.rest.resources;
import static com.google.common.base.Preconditions.checkNotNull;
-import static javax.ws.rs.core.Response.Status.ACCEPTED;
import static javax.ws.rs.core.Response.created;
import static javax.ws.rs.core.Response.status;
+import static javax.ws.rs.core.Response.Status.ACCEPTED;
import static
org.apache.brooklyn.rest.util.WebResourceUtils.serviceAbsoluteUriBuilder;
import java.net.URI;
@@ -32,6 +32,8 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
+import java.util.regex.Pattern;
+
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
@@ -58,6 +60,7 @@ import
org.apache.brooklyn.core.mgmt.entitlement.EntitlementPredicates;
import org.apache.brooklyn.core.mgmt.entitlement.Entitlements;
import org.apache.brooklyn.core.mgmt.entitlement.Entitlements.EntityAndItem;
import
org.apache.brooklyn.core.mgmt.entitlement.Entitlements.StringAndArgument;
+import org.apache.brooklyn.core.mgmt.internal.IdAlreadyExistsException;
import org.apache.brooklyn.core.sensor.Sensors;
import org.apache.brooklyn.core.typereg.RegisteredTypeLoadingContexts;
import org.apache.brooklyn.core.typereg.RegisteredTypes;
@@ -87,6 +90,7 @@ import org.apache.brooklyn.util.time.Duration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableMap;
@@ -101,6 +105,14 @@ public class ApplicationResource extends
AbstractBrooklynRestResource implements
@Context
private UriInfo uriInfo;
+ /**
+ * Only lower-case letters and digits; min 10 chars; max 1024 chars.
+ *
+ * We are this extreme because some existing entity implementations rely
on the entity-id format
+ * for use in hostnames, etc.
+ */
+ private final Pattern appIdPattern = Pattern.compile("[a-z0-9]{10,1022}");
+
private EntityDetail fromEntity(Entity entity) {
Boolean serviceUp = entity.getAttribute(Attributes.SERVICE_UP);
@@ -240,6 +252,18 @@ public class ApplicationResource extends
AbstractBrooklynRestResource implements
@Override
public Response createFromYaml(String yaml) {
+ return createFromYaml(yaml, Optional.absent());
+ }
+
+ @Override
+ public Response createFromYamlWithAppId(String yaml, String appId) {
+ if (!appIdPattern.matcher(appId).matches()) {
+ throw new IllegalArgumentException("Invalid appId '"+appId+"'");
+ }
+ return createFromYaml(yaml, Optional.of(appId));
+ }
+
+ protected Response createFromYaml(String yaml, Optional<String> appId) {
// First of all, see if it's a URL
Preconditions.checkNotNull(yaml, "Blueprint must not be null");
URI uri = null;
@@ -284,7 +308,10 @@ public class ApplicationResource extends
AbstractBrooklynRestResource implements
}
try {
- return launch(yaml, spec);
+ return launch(yaml, spec, appId);
+ } catch (IdAlreadyExistsException e) {
+ log.warn("Failed REST deployment launching "+spec+": "+e);
+ throw
WebResourceUtils.throwWebApplicationException(Response.Status.CONFLICT, e,
"Error launching blueprint, id already exists");
} catch (Exception e) {
Exceptions.propagateIfFatal(e);
log.warn("Failed REST deployment launching "+spec+": "+e);
@@ -292,9 +319,9 @@ public class ApplicationResource extends
AbstractBrooklynRestResource implements
}
}
- private Response launch(String yaml, EntitySpec<? extends Application>
spec) {
+ private Response launch(String yaml, EntitySpec<? extends Application>
spec, Optional<String> entityId) {
try {
- Application app = EntityManagementUtils.createUnstarted(mgmt(),
spec);
+ Application app = EntityManagementUtils.createUnstarted(mgmt(),
spec, entityId);
boolean isEntitled = Entitlements.isEntitled(
mgmt().getEntitlementManager(),
@@ -374,7 +401,7 @@ public class ApplicationResource extends
AbstractBrooklynRestResource implements
if (spec != null) {
try {
- return launch(potentialYaml, spec);
+ return launch(potentialYaml, spec, Optional.absent());
} catch (Exception e) {
Exceptions.propagateIfFatal(e);
log.warn("Failed REST deployment launching "+spec+": "+e);
@@ -409,7 +436,7 @@ public class ApplicationResource extends
AbstractBrooklynRestResource implements
private EntitySpec<? extends Application>
createEntitySpecForApplication(String potentialYaml) {
return EntityManagementUtils.createEntitySpecForApplication(mgmt(),
potentialYaml);
}
-
+
private void checkApplicationTypesAreValid(ApplicationSpec
applicationSpec) {
String appType = applicationSpec.getType();
if (appType != null) {
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/3cd14917/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/ApplicationResourceTest.java
----------------------------------------------------------------------
diff --git
a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/ApplicationResourceTest.java
b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/ApplicationResourceTest.java
index 2a53cc9..679553d 100644
---
a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/ApplicationResourceTest.java
+++
b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/ApplicationResourceTest.java
@@ -21,10 +21,13 @@ package org.apache.brooklyn.rest.resources;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Iterables.find;
import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
import java.io.IOException;
+import java.io.InputStream;
import java.net.URI;
import java.util.Collection;
import java.util.List;
@@ -50,6 +53,7 @@ import org.apache.brooklyn.core.location.AbstractLocation;
import org.apache.brooklyn.core.location.LocationConfigKeys;
import org.apache.brooklyn.core.location.geo.HostGeoInfo;
import org.apache.brooklyn.core.location.internal.LocationInternal;
+import org.apache.brooklyn.core.mgmt.internal.IdAlreadyExistsException;
import org.apache.brooklyn.entity.stock.BasicApplication;
import org.apache.brooklyn.entity.stock.BasicEntity;
import org.apache.brooklyn.rest.domain.ApiError;
@@ -74,6 +78,8 @@ import org.apache.brooklyn.test.Asserts;
import org.apache.brooklyn.util.collections.CollectionFunctionals;
import org.apache.brooklyn.util.exceptions.Exceptions;
import org.apache.brooklyn.util.http.HttpAsserts;
+import org.apache.brooklyn.util.stream.Streams;
+import org.apache.brooklyn.util.text.StringPredicates;
import org.apache.brooklyn.util.text.Strings;
import org.apache.cxf.jaxrs.client.WebClient;
import org.apache.http.HttpHeaders;
@@ -84,12 +90,14 @@ import org.testng.Assert;
import org.testng.annotations.Test;
import com.google.common.base.Joiner;
+import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
+import com.google.common.collect.Range;
@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
@@ -662,6 +670,88 @@ public class ApplicationResourceTest extends
BrooklynRestResourceTest {
return Iterables.getOnlyElement(itemSummaries);
}
+ @Test(dependsOnMethods = { "testDeployApplication", "testLocatedLocation"
})
+ public void testDeploymentUsesAppId() throws Exception {
+ String yaml = "{ name: simple-app-yaml, services: [ { type:
"+BasicApplication.class.getCanonicalName()+" } ] }";
+ String appId = "myidtestdeploymentuidisset";
+
+ Response response = deployApp(yaml, appId);
+ assertResponseStatus(response, 201);
+
+ BasicApplication app = (BasicApplication)
getManagementContext().getEntityManager().getEntity(appId);
+ assertNotNull(app);
+ assertEquals(app.getId(), appId);
+ }
+
+ @Test(dependsOnMethods = { "testDeployApplication", "testLocatedLocation"
})
+ public void testDeploymentFailsOnInvalidAppId() throws Exception {
+ assertAppIdValid("abcdefghijkl");
+ assertAppIdValid("1234567890");
+ assertAppIdInvalid("tooshort"); // must be at least 10 chars
+ assertAppIdInvalid("a-bcdefghijkl"); // must be letters/digits only
+ assertAppIdInvalid("Abcdefghijkl"); // must be lower-case only
+ assertAppIdInvalid(com.google.common.base.Strings.repeat("a", 1025));
// too long
+ }
+
+ private void assertAppIdInvalid(String appId) {
+ String yaml = "{ name: simple-app-yaml, services: [ { type:
"+BasicApplication.class.getCanonicalName()+" } ] }";
+ Response response = deployApp(yaml, appId);
+ assertResponseStatus(response, 400,
StringPredicates.containsLiteral("Invalid appId '"+appId+"'"));
+ assertNull(getManagementContext().getEntityManager().getEntity(appId));
+ }
+
+ private void assertAppIdValid(String appId) {
+ String yaml = "{ name: simple-app-yaml, services: [ { type:
"+BasicApplication.class.getCanonicalName()+" } ] }";
+ Response response = deployApp(yaml, appId);
+ assertResponseStatus(response, 201);
+
assertNotNull(getManagementContext().getEntityManager().getEntity(appId));
+ }
+
+ @Test(dependsOnMethods = { "testDeployApplication", "testLocatedLocation"
})
+ public void testDeploymentFailsOnDuplicateAppId() throws Exception {
+ // First app
+ String appId = "myuidtestdeploymentuidfailsonduplicate";
+ String yaml = "{ name: my-name-1, services: [ { type:
"+BasicApplication.class.getCanonicalName()+" } ] }";
+ Response response = deployApp(yaml, appId);
+ assertResponseStatus(response, 201);
+
+ BasicApplication app = (BasicApplication)
getManagementContext().getEntityManager().getEntity(appId);
+ assertNotNull(app);
+
+ // Second app should get a conflict response (409)
+ String yaml2 = "{ name: my-name-2, services: [ { type:
"+BasicApplication.class.getCanonicalName()+" } ] }";
+ Response response2 = deployApp(yaml2, appId);
+ assertResponseStatus(response2, 409,
StringPredicates.containsAllLiterals(
+ IdAlreadyExistsException.class.getSimpleName(), "already known
under that id '"+appId+"'"));
+
+ Optional<Application> app2 =
Iterables.tryFind(getManagementContext().getApplications(),
EntityPredicates.displayNameEqualTo("my-name-2"));
+ assertFalse(app2.isPresent(), "app2="+app2);
+
+ // Third app with different app id should work
+ String appId3 = "myuiddifferent";
+ String yaml3 = "{ name: my-name-3, services: [ { type:
"+BasicApplication.class.getCanonicalName()+" } ] }";
+ Response response3 = deployApp(yaml3, appId3);
+ assertResponseStatus(response3, 201);
+
+ BasicApplication app3 = (BasicApplication)
getManagementContext().getEntityManager().getEntity(appId3);
+ assertNotNull(app3);
+
+ // Delete app1; then deploying app2 should succeed
+ Entities.unmanage(app);
+
+ Response response2b = deployApp(yaml2, appId);
+ assertResponseStatus(response2b, 201);
+
+ BasicApplication app2b = (BasicApplication)
getManagementContext().getEntityManager().getEntity(appId);
+ assertNotNull(app2b);
+ assertEquals(app2b.getDisplayName(), "my-name-2");
+ }
+
+ private Response deployApp(String yaml, String appId) {
+ return client().path("/applications/"+appId)
+ .put(Entity.entity(yaml, "application/x-yaml"));
+ }
+
private void deprecateCatalogItem(String symbolicName, String version,
boolean deprecated) {
String id = String.format("%s:%s", symbolicName, version);
Response response =
client().path(String.format("/catalog/entities/%s/deprecated", id))
@@ -697,4 +787,19 @@ public class ApplicationResourceTest extends
BrooklynRestResourceTest {
client().path("/catalog").post(yaml);
}
+
+ private void assertResponseStatus(Response response, int expectedStatus,
Predicate<? super String> expectedBody) {
+ assertResponseStatus(response, Range.singleton(expectedStatus),
expectedBody);
+ }
+
+ private void assertResponseStatus(Response response, int expectedStatus) {
+ assertResponseStatus(response, Range.singleton(expectedStatus),
Predicates.alwaysTrue());
+ }
+
+ private void assertResponseStatus(Response response, Range<Integer>
expectedStatusRange, Predicate<? super String> expectedBody) {
+ String body =
Streams.readFullyString((InputStream)response.getEntity());
+ String errMsg = "response is "+response+";
status="+response.getStatus()+";
reason="+response.getStatusInfo().getReasonPhrase()+"; body="+body;
+ assertTrue(expectedStatusRange.contains(response.getStatus()), errMsg);
+ assertTrue(expectedBody.apply(body), errMsg);
+ }
}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/3cd14917/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestResourceTest.java
----------------------------------------------------------------------
diff --git
a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestResourceTest.java
b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestResourceTest.java
index 0174332..30d6697 100644
---
a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestResourceTest.java
+++
b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestResourceTest.java
@@ -24,6 +24,7 @@ import java.io.IOException;
import java.net.URI;
import java.util.Collection;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
@@ -39,6 +40,7 @@ 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.net.Urls;
import org.apache.brooklyn.util.repeat.Repeater;
import org.apache.brooklyn.util.time.Duration;
import org.apache.cxf.endpoint.Server;
@@ -162,6 +164,15 @@ public abstract class BrooklynRestResourceTest extends
BrooklynRestApiTest {
return client().path(uri).get(ApplicationSummary.class).getStatus();
}
+ protected Map<?, ?> getApplicationConfig(URI appUri) {
+ // appUri in a format like
"http://localhost:9998/applications/mwk66lso65/config/current-state";
+ // Will call /applications/{application}/entities/{entity}/config
+ String[] pathParts = appUri.getPath().split("/");
+ String appId = pathParts[pathParts.length-1];
+ URI configUri = URI.create(Urls.mergePaths(appUri.toString(),
"/entities/", appId, "/config/current-state"));
+ return client().path(configUri).get(Map.class);
+ }
+
protected void waitForPageFoundResponse(final String resource, final
Class<?> clazz) {
boolean found = Repeater.create("Wait for page found")
.until(new Callable<Boolean>() {