fineract-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From my...@apache.org
Subject [fineract-cn-provisioner] 34/50: Added activeMQ listeners to provisioner, and reordered initialization calls: Now the permittable groups are passed to identity before further initialization, and the listeners are used to ensure that that's complete before proceeding to further initialize.
Date Mon, 22 Jan 2018 15:16:32 GMT
This is an automated email from the ASF dual-hosted git repository.

myrle pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract-cn-provisioner.git

commit d7b6cf9225698681eeeb6c9d40840a62cb17758a
Author: myrle-krantz <mkrantz@mifos.org>
AuthorDate: Fri Jun 2 22:01:14 2017 +0200

    Added activeMQ listeners to provisioner, and reordered initialization calls:
    Now the permittable groups are passed to identity before further initialization, and the listeners are used to ensure that that's complete before proceeding to further initialize.
---
 .../provisioner/api/v1/domain/ApplicationTest.java |  15 ++
 .../api/v1/domain/AssignedApplicationTest.java     |  15 ++
 .../provisioner/api/v1/domain/TenantTest.java      |  15 ++
 .../io/mifos/provisioner/AbstractServiceTest.java  |   7 +-
 .../tenant/TestTenantApplicationAssignment.java    | 171 ++++++++++++++++-----
 service/build.gradle                               |   3 +
 .../config/ProvisionerActiveMQProperties.java      |  55 +++++++
 .../config/ProvisionerServiceConfig.java           |  27 ++++
 .../internal/listener/EventExpectation.java        |  87 +++++++++++
 .../provisioner/internal/listener/EventKey.java    |  57 +++++++
 .../internal/listener/IdentityListener.java        | 126 +++++++++++++++
 .../internal/service/TenantApplicationService.java |  63 ++++++--
 .../applications/IdentityServiceInitializer.java   |  62 ++++++--
 .../IdentityServiceInitializerTest.java            |  24 ++-
 shared.gradle                                      |   2 +
 15 files changed, 657 insertions(+), 72 deletions(-)

diff --git a/api/src/test/java/io/mifos/provisioner/api/v1/domain/ApplicationTest.java b/api/src/test/java/io/mifos/provisioner/api/v1/domain/ApplicationTest.java
index 96f426c..f93c86f 100644
--- a/api/src/test/java/io/mifos/provisioner/api/v1/domain/ApplicationTest.java
+++ b/api/src/test/java/io/mifos/provisioner/api/v1/domain/ApplicationTest.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed 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 io.mifos.provisioner.api.v1.domain;
 
 import io.mifos.core.test.domain.ValidationTest;
diff --git a/api/src/test/java/io/mifos/provisioner/api/v1/domain/AssignedApplicationTest.java b/api/src/test/java/io/mifos/provisioner/api/v1/domain/AssignedApplicationTest.java
index 19f4f9f..0ee0a01 100644
--- a/api/src/test/java/io/mifos/provisioner/api/v1/domain/AssignedApplicationTest.java
+++ b/api/src/test/java/io/mifos/provisioner/api/v1/domain/AssignedApplicationTest.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed 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 io.mifos.provisioner.api.v1.domain;
 
 import io.mifos.core.test.domain.ValidationTest;
diff --git a/api/src/test/java/io/mifos/provisioner/api/v1/domain/TenantTest.java b/api/src/test/java/io/mifos/provisioner/api/v1/domain/TenantTest.java
index ddf87e3..ceb8954 100644
--- a/api/src/test/java/io/mifos/provisioner/api/v1/domain/TenantTest.java
+++ b/api/src/test/java/io/mifos/provisioner/api/v1/domain/TenantTest.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed 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 io.mifos.provisioner.api.v1.domain;
 
 import io.mifos.core.test.domain.ValidationTest;
diff --git a/component-test/src/main/java/io/mifos/provisioner/AbstractServiceTest.java b/component-test/src/main/java/io/mifos/provisioner/AbstractServiceTest.java
index 1fc4cdd..f47ca69 100644
--- a/component-test/src/main/java/io/mifos/provisioner/AbstractServiceTest.java
+++ b/component-test/src/main/java/io/mifos/provisioner/AbstractServiceTest.java
@@ -17,6 +17,7 @@ package io.mifos.provisioner;
 
 import io.mifos.core.test.env.TestEnvironment;
 import io.mifos.provisioner.api.v1.client.Provisioner;
+import io.mifos.provisioner.config.ProvisionerActiveMQProperties;
 import io.mifos.provisioner.config.ProvisionerServiceConfig;
 import org.junit.BeforeClass;
 import org.junit.ClassRule;
@@ -36,7 +37,11 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
 
 @RunWith(SpringJUnit4ClassRunner.class)
 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT,
-        classes = {AbstractServiceTest.TestConfiguration.class})
+        classes = {AbstractServiceTest.TestConfiguration.class},
+        properties = {
+                ProvisionerActiveMQProperties.ACTIVEMQ_BROKER_URL_PROP + "=" + ProvisionerActiveMQProperties.ACTIVEMQ_BROKER_URL_DEFAULT,
+                ProvisionerActiveMQProperties.ACTIVEMQ_CONCURRENCY_PROP + "=" + ProvisionerActiveMQProperties.ACTIVEMQ_CONCURRENCY_DEFAULT}
+)
 public class AbstractServiceTest {
   private static final String APP_NAME = "provisioner-v1";
   private static final String CLIENT_ID = "sillyRabbit";
diff --git a/component-test/src/main/java/io/mifos/provisioner/tenant/TestTenantApplicationAssignment.java b/component-test/src/main/java/io/mifos/provisioner/tenant/TestTenantApplicationAssignment.java
index 9b46af3..a72bb38 100644
--- a/component-test/src/main/java/io/mifos/provisioner/tenant/TestTenantApplicationAssignment.java
+++ b/component-test/src/main/java/io/mifos/provisioner/tenant/TestTenantApplicationAssignment.java
@@ -15,6 +15,7 @@
  */
 package io.mifos.provisioner.tenant;
 
+import com.google.gson.Gson;
 import io.mifos.anubis.api.v1.client.Anubis;
 import io.mifos.anubis.api.v1.domain.AllowedOperation;
 import io.mifos.anubis.api.v1.domain.ApplicationSignatureSet;
@@ -22,7 +23,6 @@ import io.mifos.anubis.api.v1.domain.PermittableEndpoint;
 import io.mifos.anubis.api.v1.domain.Signature;
 import io.mifos.anubis.provider.SystemRsaKeyProvider;
 import io.mifos.anubis.test.v1.SystemSecurityEnvironment;
-import io.mifos.anubis.token.TokenSerializationResult;
 import io.mifos.core.api.context.AutoSeshat;
 import io.mifos.core.api.util.ApiConstants;
 import io.mifos.core.api.util.ApiFactory;
@@ -33,14 +33,17 @@ import io.mifos.identity.api.v1.client.IdentityManager;
 import io.mifos.identity.api.v1.domain.CallEndpointSet;
 import io.mifos.identity.api.v1.domain.Permission;
 import io.mifos.identity.api.v1.domain.PermittableGroup;
+import io.mifos.identity.api.v1.events.ApplicationSignatureEvent;
 import io.mifos.permittedfeignclient.api.v1.client.ApplicationPermissionRequirements;
 import io.mifos.permittedfeignclient.api.v1.domain.ApplicationPermission;
 import io.mifos.provisioner.ProvisionerCassandraInitializer;
 import io.mifos.provisioner.ProvisionerMariaDBInitializer;
 import io.mifos.provisioner.api.v1.client.Provisioner;
 import io.mifos.provisioner.api.v1.domain.*;
+import io.mifos.provisioner.config.ProvisionerActiveMQProperties;
 import io.mifos.provisioner.config.ProvisionerConstants;
 import io.mifos.provisioner.config.ProvisionerServiceConfig;
+import io.mifos.provisioner.internal.listener.IdentityListener;
 import io.mifos.provisioner.internal.service.applications.ApplicationCallContextProvider;
 import io.mifos.provisioner.internal.util.TokenProvider;
 import org.junit.*;
@@ -66,7 +69,6 @@ import java.math.BigInteger;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
-import java.util.concurrent.TimeUnit;
 
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.*;
@@ -75,7 +77,11 @@ import static org.mockito.Mockito.*;
  * @author Myrle Krantz
  */
 @RunWith(SpringJUnit4ClassRunner.class)
-@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT,
+        properties = {
+                ProvisionerActiveMQProperties.ACTIVEMQ_BROKER_URL_PROP + "=" + ProvisionerActiveMQProperties.ACTIVEMQ_BROKER_URL_DEFAULT,
+                ProvisionerActiveMQProperties.ACTIVEMQ_CONCURRENCY_PROP + "=" + ProvisionerActiveMQProperties.ACTIVEMQ_CONCURRENCY_DEFAULT}
+)
 public class TestTenantApplicationAssignment {
   private static final String APP_NAME = "provisioner-v1";
   private static final String CLIENT_ID = "sillyRabbit";
@@ -135,6 +141,12 @@ public class TestTenantApplicationAssignment {
   @Autowired
   protected SystemRsaKeyProvider systemRsaKeyProvider;
 
+  @Autowired
+  protected IdentityListener identityListener;
+
+  @Autowired
+  protected Gson gson;
+
   private AutoSeshat autoSeshat;
 
   public TestTenantApplicationAssignment() {
@@ -164,17 +176,6 @@ public class TestTenantApplicationAssignment {
     autoSeshat.close();
   }
 
-  private static class TokenChecker implements Answer<TokenSerializationResult> {
-    TokenSerializationResult result = null;
-
-    @Override
-    public TokenSerializationResult answer(final InvocationOnMock invocation) throws Throwable {
-      result = (TokenSerializationResult) invocation.callRealMethod();
-      Assert.assertNotNull(result);
-      return result;
-    }
-  }
-
   private class VerifyIsisInitializeContext implements Answer<ApplicationSignatureSet> {
 
     private final String keyTimestamp;
@@ -211,6 +212,10 @@ public class TestTenantApplicationAssignment {
       return ret;
     }
 
+    String getKeyTimestamp() {
+      return keyTimestamp;
+    }
+
     boolean isValidSecurityContext() {
       return validSecurityContext;
     }
@@ -314,13 +319,103 @@ public class TestTenantApplicationAssignment {
     }
   }
 
+  private class VerifyIsisCreatePermittableGroup implements Answer<Void> {
+
+    private boolean validSecurityContext = true;
+    private final String tenantIdentifier;
+    private int callCount = 0;
+
+    private VerifyIsisCreatePermittableGroup(final String tenantIdentifier) {
+      this.tenantIdentifier = tenantIdentifier;
+    }
+
+    @Override
+    public Void answer(final InvocationOnMock invocation) throws Throwable {
+      final boolean validSecurityContextForThisCall = systemSecurityEnvironment.isValidSystemSecurityContext("identity", "1", tenantIdentifier);
+      validSecurityContext = validSecurityContext && validSecurityContextForThisCall;
+      callCount++;
+
+      final PermittableGroup arg = invocation.getArgumentAt(0, PermittableGroup.class);
+      identityListener.onCreatePermittableGroup(tenantIdentifier, arg.getIdentifier());
+      return null;
+    }
+
+    boolean isValidSecurityContext() {
+      return validSecurityContext;
+    }
+
+    int getCallCount() {
+      return callCount;
+    }
+  }
+
+  private class VerifyIsisSetApplicationSignature implements Answer<Void> {
+
+    private boolean validSecurityContext = false;
+    private final String tenantIdentifier;
+
+    private VerifyIsisSetApplicationSignature(final String tenantIdentifier) {
+      this.tenantIdentifier = tenantIdentifier;
+    }
+
+    @Override
+    public Void answer(final InvocationOnMock invocation) throws Throwable {
+      validSecurityContext = systemSecurityEnvironment.isValidSystemSecurityContext("identity", "1", tenantIdentifier);
+
+      final String applicationIdentifier = invocation.getArgumentAt(0, String.class);
+      final String keyTimestamp = invocation.getArgumentAt(1, String.class);
+      identityListener.onSetApplicationSignature(tenantIdentifier,
+              gson.toJson(new ApplicationSignatureEvent(applicationIdentifier, keyTimestamp)));
+      return null;
+    }
+
+    boolean isValidSecurityContext() {
+      return validSecurityContext;
+    }
+  }
+
+  private class VerifyIsisCreateApplicationPermission implements Answer<Void> {
+
+    private boolean validSecurityContext = true;
+    private final String tenantIdentifier;
+    private final String applicationIdentifier;
+    private int callCount = 0;
+
+    private VerifyIsisCreateApplicationPermission(final String tenantIdentifier, final String applicationIdentifier) {
+      this.tenantIdentifier = tenantIdentifier;
+      this.applicationIdentifier = applicationIdentifier;
+    }
+
+    @Override
+    public Void answer(final InvocationOnMock invocation) throws Throwable {
+      final boolean validSecurityContextForThisCall = systemSecurityEnvironment.isValidSystemSecurityContext("identity", "1", tenantIdentifier);
+      validSecurityContext = validSecurityContext && validSecurityContextForThisCall;
+      callCount++;
+
+      final String callApplicationIdentifier = invocation.getArgumentAt(0, String.class);
+      Assert.assertEquals(this.applicationIdentifier, callApplicationIdentifier);
+      return null;
+    }
+
+
+
+    boolean isValidSecurityContext() {
+      return validSecurityContext;
+    }
+
+    int getCallCount() {
+      return callCount;
+    }
+  }
+
   @Test
-  public void testTenantApplicationAssignment() throws InterruptedException {
+  public void testTenantApplicationAssignment() throws Exception {
     //Create io.mifos.provisioner.tenant
     final Tenant tenant = Fixture.getCompTestTenant();
     provisioner.createTenant(tenant);
 
 
+
     //Create identity service application
     final Application identityServiceApp = new Application();
     identityServiceApp.setName("identity-v1");
@@ -348,9 +443,6 @@ public class TestTenantApplicationAssignment {
     }
     doAnswer(verifyInitializeContextAndReturnSignature).when(identityServiceMock).initialize(anyString());
 
-    final TokenChecker tokenChecker = new TokenChecker();
-    doAnswer(tokenChecker).when(tokenProviderSpy).createToken(tenant.getIdentifier(), "identity-v1", 2L, TimeUnit.MINUTES);
-
     {
       final IdentityManagerInitialization identityServiceAdminInitialization
               = provisioner.assignIdentityManager(tenant.getIdentifier(), identityServiceAssigned);
@@ -360,9 +452,6 @@ public class TestTenantApplicationAssignment {
       Assert.assertNotNull(identityServiceAdminInitialization.getAdminPassword());
     }
 
-    verify(applicationCallContextProviderSpy, atMost(2)).getApplicationCallContext(tenant.getIdentifier(), "identity-v1");
-
-
     //Create horus application.
     final Application officeApp = new Application();
     officeApp.setName("office-v1");
@@ -397,43 +486,47 @@ public class TestTenantApplicationAssignment {
     final VerifyCreateSignatureSetContext verifyCreateSignatureSetContext;
     final VerifyAnubisPermittablesContext verifyAnubisPermittablesContext;
     final VerifyAnputRequiredPermissionsContext verifyAnputRequiredPermissionsContext;
+    final VerifyIsisCreatePermittableGroup verifyIsisCreatePermittableGroup;
+    final VerifyIsisSetApplicationSignature verifyIsisSetApplicationSignature;
+    final VerifyIsisCreateApplicationPermission verifyIsisCreateApplicationPermission;
     try (final AutoTenantContext ignored = new AutoTenantContext(tenant.getIdentifier())) {
       verifyAnubisInitializeContext = new VerifyAnubisInitializeContext("office", tenant.getIdentifier());
       verifyCreateSignatureSetContext = new VerifyCreateSignatureSetContext(keysInApplicationSignature, "office", tenant.getIdentifier());
       verifyAnubisPermittablesContext = new VerifyAnubisPermittablesContext(Arrays.asList(xxPermittableEndpoint, xxPermittableEndpoint, xyPermittableEndpoint, xyGetPermittableEndpoint, mPermittableEndpoint), tenant.getIdentifier());
       verifyAnputRequiredPermissionsContext = new VerifyAnputRequiredPermissionsContext(Arrays.asList(forFooPermission, forBarPermission), tenant.getIdentifier());
+      verifyIsisCreatePermittableGroup = new VerifyIsisCreatePermittableGroup(tenant.getIdentifier());
+      verifyIsisSetApplicationSignature = new VerifyIsisSetApplicationSignature(tenant.getIdentifier());
+      verifyIsisCreateApplicationPermission = new VerifyIsisCreateApplicationPermission(tenant.getIdentifier(), "office-v1");
     }
     doAnswer(verifyAnubisInitializeContext).when(anubisMock).initializeResources();
     doAnswer(verifyCreateSignatureSetContext).when(anubisMock).createSignatureSet(anyString(), anyObject());
     doAnswer(verifyAnubisPermittablesContext).when(anubisMock).getPermittableEndpoints();
     doAnswer(verifyAnputRequiredPermissionsContext).when(anputMock).getRequiredPermissions();
-
+    doAnswer(verifyIsisCreatePermittableGroup).when(identityServiceMock).createPermittableGroup(new PermittableGroup("x", Arrays.asList(xxPermittableEndpoint, xyPermittableEndpoint, xyGetPermittableEndpoint)));
+    doAnswer(verifyIsisCreatePermittableGroup).when(identityServiceMock).createPermittableGroup(new PermittableGroup("m", Collections.singletonList(mPermittableEndpoint)));
+    doAnswer(verifyIsisSetApplicationSignature).when(identityServiceMock).setApplicationSignature(
+            "office-v1",
+            verifyInitializeContextAndReturnSignature.getKeyTimestamp(),
+            new Signature(keysInApplicationSignature.getPublicKeyMod(), keysInApplicationSignature.getPublicKeyExp()));
+    doAnswer(verifyIsisCreateApplicationPermission).when(identityServiceMock).createApplicationPermission("office-v1", new Permission("x", AllowedOperation.ALL));
+    doAnswer(verifyIsisCreateApplicationPermission).when(identityServiceMock).createApplicationPermission("office-v1", new Permission("m", Collections.singleton(AllowedOperation.READ)));
+    doAnswer(verifyIsisCreateApplicationPermission).when(identityServiceMock).createApplicationCallEndpointSet("office-v1", new CallEndpointSet("forPurposeFoo", Collections.singletonList("x")));
+    doAnswer(verifyIsisCreateApplicationPermission).when(identityServiceMock).createApplicationCallEndpointSet("office-v1", new CallEndpointSet("forPurposeBar", Collections.singletonList("m")));
 
     {
       provisioner.assignApplications(tenant.getIdentifier(), Collections.singletonList(officeAssigned));
-      Thread.sleep(1500L); //Application assigning is asynchronous and I have no message queue.
-    }
-
-    verify(applicationCallContextProviderSpy).getApplicationCallContext(tenant.getIdentifier(), "office-v1");
-    verify(applicationCallContextProviderSpy, never()).getApplicationCallContext(eq(Fixture.TENANT_NAME), Mockito.anyString());
-    verify(tokenProviderSpy).createToken(tenant.getIdentifier(), "office-v1", 2L, TimeUnit.MINUTES);
 
-    try (final AutoTenantContext ignored = new AutoTenantContext(tenant.getIdentifier())) {
-      verify(identityServiceMock).setApplicationSignature(
-              "office-v1",
-              systemSecurityEnvironment.tenantKeyTimestamp(),
-              new Signature(keysInApplicationSignature.getPublicKeyMod(), keysInApplicationSignature.getPublicKeyExp()));
-      verify(identityServiceMock).createPermittableGroup(new PermittableGroup("x", Arrays.asList(xxPermittableEndpoint, xyPermittableEndpoint, xyGetPermittableEndpoint)));
-      verify(identityServiceMock).createPermittableGroup(new PermittableGroup("m", Collections.singletonList(mPermittableEndpoint)));
-      verify(identityServiceMock).createApplicationPermission("office-v1", new Permission("x", AllowedOperation.ALL));
-      verify(identityServiceMock).createApplicationPermission("office-v1", new Permission("m", Collections.singleton(AllowedOperation.READ)));
-      verify(identityServiceMock).createApplicationCallEndpointSet("office-v1", new CallEndpointSet("forPurposeFoo", Collections.singletonList("x")));
-      verify(identityServiceMock).createApplicationCallEndpointSet("office-v1", new CallEndpointSet("forPurposeBar", Collections.singletonList("m")));
+      Thread.sleep(1500L); //Application assigning is asynchronous and I have no message queue.
     }
 
     Assert.assertTrue(verifyAnubisInitializeContext.isValidSecurityContext());
     Assert.assertTrue(verifyCreateSignatureSetContext.isValidSecurityContext());
     Assert.assertTrue(verifyAnubisPermittablesContext.isValidSecurityContext());
     Assert.assertTrue(verifyAnputRequiredPermissionsContext.isValidSecurityContext());
+    Assert.assertEquals(2, verifyIsisCreatePermittableGroup.getCallCount());
+    Assert.assertTrue(verifyIsisCreatePermittableGroup.isValidSecurityContext());
+    Assert.assertTrue(verifyIsisSetApplicationSignature.isValidSecurityContext());
+    Assert.assertEquals(4, verifyIsisCreateApplicationPermission.getCallCount());
+    Assert.assertTrue(verifyIsisCreateApplicationPermission.isValidSecurityContext());
   }
 }
diff --git a/service/build.gradle b/service/build.gradle
index edb1055..2bd249e 100644
--- a/service/build.gradle
+++ b/service/build.gradle
@@ -30,6 +30,9 @@ dependencies {
             [group: 'org.springframework.cloud', name: 'spring-cloud-starter-config'],
             [group: 'org.springframework.cloud', name: 'spring-cloud-starter-eureka'],
             [group: 'org.springframework.boot', name: 'spring-boot-starter-jetty'],
+            [group: 'org.springframework', name: 'spring-jms', version: versions.springcontext],
+            [group: 'org.apache.activemq', name: 'activemq-broker', version: versions.activeMQ],
+            [group: 'org.apache.activemq', name: 'activemq-spring', version: versions.activeMQ],
             [group: 'io.mifos.provisioner', name: 'api', version: project.version],
             [group: 'io.mifos.anubis', name: 'library', version: versions.frameworkanubis],
             [group: 'io.mifos.anubis', name: 'api', version: versions.frameworkanubis],
diff --git a/service/src/main/java/io/mifos/provisioner/config/ProvisionerActiveMQProperties.java b/service/src/main/java/io/mifos/provisioner/config/ProvisionerActiveMQProperties.java
new file mode 100644
index 0000000..640b921
--- /dev/null
+++ b/service/src/main/java/io/mifos/provisioner/config/ProvisionerActiveMQProperties.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed 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 io.mifos.provisioner.config;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * @author Myrle Krantz
+ */
+@ConfigurationProperties(prefix = "activemq")
+public class ProvisionerActiveMQProperties {
+  @SuppressWarnings("unused")
+  final public static String ACTIVEMQ_BROKER_URL_PROP = "activemq.brokerUrl";
+  @SuppressWarnings("unused")
+  final public static String ACTIVEMQ_CONCURRENCY_PROP = "activemq.concurrency";
+  @SuppressWarnings("WeakerAccess")
+  final public static String ACTIVEMQ_BROKER_URL_DEFAULT = "vm://localhost?broker.persistent=false";
+  @SuppressWarnings("WeakerAccess")
+  final public static String ACTIVEMQ_CONCURRENCY_DEFAULT = "3-10";
+
+  private String brokerUrl = ACTIVEMQ_BROKER_URL_DEFAULT;
+  private String concurrency = ACTIVEMQ_CONCURRENCY_DEFAULT;
+
+  public ProvisionerActiveMQProperties() {
+  }
+
+  public String getBrokerUrl() {
+    return brokerUrl;
+  }
+
+  public void setBrokerUrl(String brokerUrl) {
+    this.brokerUrl = brokerUrl;
+  }
+
+  public String getConcurrency() {
+    return concurrency;
+  }
+
+  public void setConcurrency(String concurrency) {
+    this.concurrency = concurrency;
+  }
+}
diff --git a/service/src/main/java/io/mifos/provisioner/config/ProvisionerServiceConfig.java b/service/src/main/java/io/mifos/provisioner/config/ProvisionerServiceConfig.java
index 2d80cce..05b849a 100644
--- a/service/src/main/java/io/mifos/provisioner/config/ProvisionerServiceConfig.java
+++ b/service/src/main/java/io/mifos/provisioner/config/ProvisionerServiceConfig.java
@@ -26,14 +26,19 @@ import io.mifos.core.lang.config.EnableServiceException;
 import io.mifos.core.mariadb.config.EnableMariaDB;
 import io.mifos.provisioner.internal.util.TokenProvider;
 import io.mifos.tool.crypto.config.EnableCrypto;
+import org.apache.activemq.jms.pool.PooledConnectionFactory;
+import org.apache.activemq.spring.ActiveMQConnectionFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.core.env.Environment;
+import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
+import org.springframework.jms.config.JmsListenerContainerFactory;
 import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
 
@@ -43,6 +48,7 @@ import java.math.BigInteger;
 @EnableAutoConfiguration
 @ComponentScan({
     "io.mifos.provisioner.internal.service",
+    "io.mifos.provisioner.internal.listener",
     "io.mifos.provisioner.internal.service.applications",
     "io.mifos.provisioner.internal.repository",
     "io.mifos.provisioner.rest.controller",
@@ -54,6 +60,7 @@ import java.math.BigInteger;
 @EnableCassandra
 @EnableServiceException
 @EnableApplicationName
+@EnableConfigurationProperties({ProvisionerActiveMQProperties.class})
 public class ProvisionerServiceConfig extends WebMvcConfigurerAdapter {
 
   public ProvisionerServiceConfig() {
@@ -86,4 +93,24 @@ public class ProvisionerServiceConfig extends WebMvcConfigurerAdapter {
   public void configurePathMatch(final PathMatchConfigurer configurer) {
     configurer.setUseSuffixPatternMatch(Boolean.FALSE);
   }
+
+  @Bean
+  public PooledConnectionFactory jmsFactory(final ProvisionerActiveMQProperties activeMQProperties) {
+    final PooledConnectionFactory pooledConnectionFactory = new PooledConnectionFactory();
+    final ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory();
+    activeMQConnectionFactory.setBrokerURL(activeMQProperties.getBrokerUrl());
+    pooledConnectionFactory.setConnectionFactory(activeMQConnectionFactory);
+
+    return pooledConnectionFactory;
+  }
+
+
+  @Bean
+  public JmsListenerContainerFactory jmsListenerContainerFactory(final PooledConnectionFactory jmsFactory, final ProvisionerActiveMQProperties activeMQProperties) {
+    final DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
+    factory.setPubSubDomain(true);
+    factory.setConnectionFactory(jmsFactory);
+    factory.setConcurrency(activeMQProperties.getConcurrency());
+    return factory;
+  }
 }
diff --git a/service/src/main/java/io/mifos/provisioner/internal/listener/EventExpectation.java b/service/src/main/java/io/mifos/provisioner/internal/listener/EventExpectation.java
new file mode 100644
index 0000000..23d0e99
--- /dev/null
+++ b/service/src/main/java/io/mifos/provisioner/internal/listener/EventExpectation.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed 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 io.mifos.provisioner.internal.listener;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * @author Myrle Krantz
+ */
+public class EventExpectation {
+  private final EventKey key;
+  private boolean eventFound = false;
+  private boolean eventWithdrawn = false;
+
+  private final ReentrantLock lock = new ReentrantLock();
+
+  private final Condition found = lock.newCondition();
+
+  EventExpectation(final EventKey key) {
+    this.key = key;
+  }
+
+  EventKey getKey() {
+    return key;
+  }
+
+  void setEventFound(boolean eventFound) {
+    lock.lock();
+    try {
+      this.eventFound = eventFound;
+      found.signal();
+    }
+    finally {
+      lock.unlock();
+    }
+  }
+
+  void setEventWithdrawn(boolean eventWithdrawn) {
+    lock.lock();
+    try {
+      this.eventWithdrawn = eventWithdrawn;
+      found.signal();
+    }
+    finally {
+      lock.unlock();
+    }
+  }
+
+  public boolean waitForOccurrence(long timeout, TimeUnit timeUnit) throws InterruptedException {
+
+    lock.lock();
+    try {
+      if (eventFound)
+      return true;
+
+      if (eventWithdrawn)
+        return false;
+
+      found.await(timeout, timeUnit);
+
+      return (eventFound);
+    }
+    finally {
+      lock.unlock();
+    }
+  }
+
+  @Override
+  public String toString() {
+    return key.toString();
+  }
+}
diff --git a/service/src/main/java/io/mifos/provisioner/internal/listener/EventKey.java b/service/src/main/java/io/mifos/provisioner/internal/listener/EventKey.java
new file mode 100644
index 0000000..13f985e
--- /dev/null
+++ b/service/src/main/java/io/mifos/provisioner/internal/listener/EventKey.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed 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 io.mifos.provisioner.internal.listener;
+
+import java.util.Objects;
+
+/**
+ * @author Myrle Krantz
+ */
+class EventKey {
+  private String tenantIdentifier;
+  private String eventName;
+  private Object event;
+
+  EventKey(final String tenantIdentifier, final String eventName, final Object event) {
+    this.tenantIdentifier = tenantIdentifier;
+    this.eventName = eventName;
+    this.event = event;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    EventKey eventKey = (EventKey) o;
+    return Objects.equals(tenantIdentifier, eventKey.tenantIdentifier) &&
+            Objects.equals(eventName, eventKey.eventName) &&
+            Objects.equals(event, eventKey.event);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(tenantIdentifier, eventName, event);
+  }
+
+  @Override
+  public String toString() {
+    return "EventKey{" +
+            "tenantIdentifier='" + tenantIdentifier + '\'' +
+            ", eventName='" + eventName + '\'' +
+            ", event=" + event +
+            '}';
+  }
+}
diff --git a/service/src/main/java/io/mifos/provisioner/internal/listener/IdentityListener.java b/service/src/main/java/io/mifos/provisioner/internal/listener/IdentityListener.java
new file mode 100644
index 0000000..fb4d638
--- /dev/null
+++ b/service/src/main/java/io/mifos/provisioner/internal/listener/IdentityListener.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed 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 io.mifos.provisioner.internal.listener;
+
+import com.google.gson.Gson;
+import io.mifos.core.lang.config.TenantHeaderFilter;
+import io.mifos.identity.api.v1.events.ApplicationPermissionEvent;
+import io.mifos.identity.api.v1.events.ApplicationSignatureEvent;
+import io.mifos.identity.api.v1.events.EventConstants;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jms.annotation.JmsListener;
+import org.springframework.messaging.handler.annotation.Header;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static io.mifos.identity.api.v1.events.EventConstants.OPERATION_POST_APPLICATION_PERMISSION;
+import static io.mifos.identity.api.v1.events.EventConstants.OPERATION_POST_PERMITTABLE_GROUP;
+import static io.mifos.identity.api.v1.events.EventConstants.OPERATION_PUT_APPLICATION_SIGNATURE;
+
+/**
+ * @author Myrle Krantz
+ */
+@Component
+public class IdentityListener {
+  private final Gson gson;
+  private final Map<EventKey, EventExpectation> eventExpectations = new ConcurrentHashMap<>();
+
+  @Autowired
+  public IdentityListener(final Gson gson) {
+    this.gson = gson;
+  }
+
+  @JmsListener(
+          subscription = EventConstants.DESTINATION,
+          destination = EventConstants.DESTINATION,
+          selector = EventConstants.SELECTOR_POST_PERMITTABLE_GROUP
+  )
+  public void onCreatePermittableGroup(
+          @Header(TenantHeaderFilter.TENANT_HEADER)final String tenantIdentifier,
+          final String payload) throws Exception {
+    final EventExpectation eventExpectation = eventExpectations.remove(new EventKey(tenantIdentifier, OPERATION_POST_PERMITTABLE_GROUP, payload));
+    if (eventExpectation != null) {
+      eventExpectation.setEventFound(true);
+    }
+  }
+
+  @JmsListener(
+          subscription = EventConstants.DESTINATION,
+          destination = EventConstants.DESTINATION,
+          selector = EventConstants.SELECTOR_POST_APPLICATION_PERMISSION
+  )
+  public void onCreateApplicationPermission(
+          @Header(TenantHeaderFilter.TENANT_HEADER)final String tenantIdentifier,
+          final String payload) throws Exception {
+    final ApplicationPermissionEvent event = gson.fromJson(payload, ApplicationPermissionEvent.class);
+    final EventExpectation eventExpectation = eventExpectations.remove(new EventKey(tenantIdentifier, OPERATION_POST_APPLICATION_PERMISSION, event));
+    if (eventExpectation != null) {
+      eventExpectation.setEventFound(true);
+    }
+  }
+
+  @JmsListener(
+          subscription = EventConstants.DESTINATION,
+          destination = EventConstants.DESTINATION,
+          selector = EventConstants.SELECTOR_PUT_APPLICATION_SIGNATURE
+  )
+  public void onSetApplicationSignature(
+          @Header(TenantHeaderFilter.TENANT_HEADER)final String tenantIdentifier,
+          final String payload) throws Exception {
+    final ApplicationSignatureEvent event = gson.fromJson(payload, ApplicationSignatureEvent.class);
+    final EventExpectation eventExpectation = eventExpectations.remove(new EventKey(tenantIdentifier, OPERATION_PUT_APPLICATION_SIGNATURE, event));
+    if (eventExpectation != null) {
+      eventExpectation.setEventFound(true);
+    }
+  }
+
+  public EventExpectation expectPermittableGroupCreation(final String tenantIdentifier,
+                                                         final String permittableGroupIdentifier) {
+    final EventKey key = new EventKey(tenantIdentifier, OPERATION_POST_PERMITTABLE_GROUP, permittableGroupIdentifier);
+    final EventExpectation value = new EventExpectation(key);
+    eventExpectations.put(key, value);
+    return value;
+  }
+
+  public EventExpectation expectApplicationPermissionCreation(final String tenantIdentifier,
+                                                              final String applicationIdentifier,
+                                                              final String permittableGroupIdentifier) {
+    final ApplicationPermissionEvent expectedEvent = new ApplicationPermissionEvent(applicationIdentifier, permittableGroupIdentifier);
+    final EventKey key = new EventKey(tenantIdentifier, OPERATION_POST_APPLICATION_PERMISSION, expectedEvent);
+    final EventExpectation value = new EventExpectation(key);
+    eventExpectations.put(key, value);
+    return value;
+  }
+
+  public EventExpectation expectApplicationSignatureSet(final String tenantIdentifier,
+                                                        final String applicationIdentifier,
+                                                        final String keyTimestamp) {
+    final ApplicationSignatureEvent expectedEvent = new ApplicationSignatureEvent(applicationIdentifier, keyTimestamp);
+    final EventKey key = new EventKey(tenantIdentifier, OPERATION_PUT_APPLICATION_SIGNATURE, expectedEvent);
+    final EventExpectation value = new EventExpectation(key);
+    eventExpectations.put(key, value);
+    return value;
+  }
+
+  public void withdrawExpectation(final EventExpectation eventExpectation) {
+    final EventExpectation expectation = eventExpectations.remove(eventExpectation.getKey());
+    if (expectation != null) {
+      eventExpectation.setEventWithdrawn(true);
+    }
+  }
+}
diff --git a/service/src/main/java/io/mifos/provisioner/internal/service/TenantApplicationService.java b/service/src/main/java/io/mifos/provisioner/internal/service/TenantApplicationService.java
index 149f062..cc073b4 100644
--- a/service/src/main/java/io/mifos/provisioner/internal/service/TenantApplicationService.java
+++ b/service/src/main/java/io/mifos/provisioner/internal/service/TenantApplicationService.java
@@ -23,23 +23,26 @@ import io.mifos.anubis.config.TenantSignatureRepository;
 import io.mifos.core.cassandra.core.CassandraSessionProvider;
 import io.mifos.core.lang.AutoTenantContext;
 import io.mifos.core.lang.ServiceException;
+import io.mifos.provisioner.config.ProvisionerConstants;
+import io.mifos.provisioner.internal.listener.EventExpectation;
 import io.mifos.provisioner.internal.repository.ApplicationEntity;
 import io.mifos.provisioner.internal.repository.TenantApplicationEntity;
 import io.mifos.provisioner.internal.repository.TenantCassandraRepository;
 import io.mifos.provisioner.internal.repository.TenantEntity;
 import io.mifos.provisioner.internal.service.applications.AnubisInitializer;
 import io.mifos.provisioner.internal.service.applications.IdentityServiceInitializer;
+import org.slf4j.Logger;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Component;
 import org.springframework.util.Assert;
 
 import javax.annotation.Nonnull;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 @Component
 public class TenantApplicationService {
@@ -49,19 +52,22 @@ public class TenantApplicationService {
   private final IdentityServiceInitializer identityServiceInitializer;
   private final TenantSignatureRepository tenantSignatureRepository;
   private final TenantCassandraRepository tenantCassandraRepository;
+  private final Logger logger;
 
   @Autowired
   public TenantApplicationService(final CassandraSessionProvider cassandraSessionProvider,
                                   final AnubisInitializer anubisInitializer,
                                   final IdentityServiceInitializer identityServiceInitializer,
                                   @SuppressWarnings("SpringJavaAutowiringInspection") final TenantSignatureRepository tenantSignatureRepository,
-                                  final TenantCassandraRepository tenantCassandraRepository) {
+                                  final TenantCassandraRepository tenantCassandraRepository,
+                                  @Qualifier(ProvisionerConstants.LOGGER_NAME) final Logger logger) {
     super();
     this.cassandraSessionProvider = cassandraSessionProvider;
     this.anubisInitializer = anubisInitializer;
     this.identityServiceInitializer = identityServiceInitializer;
     this.tenantSignatureRepository = tenantSignatureRepository;
     this.tenantCassandraRepository = tenantCassandraRepository;
+    this.logger = logger;
   }
 
   @Async
@@ -79,25 +85,58 @@ public class TenantApplicationService {
     final Set<ApplicationNameToUriPair> applicationNameToUriPairs =
             getApplicationNameToUriPairs(tenantApplicationEntity, appNameToUriMap);
 
-    getLatestIdentityManagerSignatureSet(tenantEntity)
-            .ifPresent(y -> initializeSecurity(tenantEntity, y, applicationNameToUriPairs));
+    final Optional<ApplicationSignatureSet> latestIdentityManagerSignatureSet = getLatestIdentityManagerSignatureSet(tenantEntity);
+    latestIdentityManagerSignatureSet.ifPresent(y -> {
+              try {
+                initializeSecurity(tenantEntity, y, applicationNameToUriPairs);
+              } catch (final InterruptedException e) {
+                logger.error("Because of interruption, started but didn't finish application assignment for {} in tenant {}.",
+                        appNameToUriMap.keySet(), tenantApplicationEntity.getTenantIdentifier(), e);
+              }
+            });
+    if (!latestIdentityManagerSignatureSet.isPresent()) {
+      logger.warn("No identity manager signature is available, so security is not initialized for applications {}",
+              appNameToUriMap.keySet());
+    }
   }
 
   private void initializeSecurity(final TenantEntity tenantEntity,
                                   final ApplicationSignatureSet identityManagerSignatureSet,
-                                  final Set<ApplicationNameToUriPair> applicationNameToUriPairs) {
+                                  final Set<ApplicationNameToUriPair> applicationNameToUriPairs) throws InterruptedException {
+    final String tenantIdentifier = tenantEntity.getIdentifier();
+    final String identityManagerApplicationName = tenantEntity.getIdentityManagerApplicationName();
+    final String identityManagerApplicationUri = tenantEntity.getIdentityManagerApplicationUri();
+
+    //Permittable groups must be posted before resource initialization occurs because resource initialization
+    //may request callback from another service. For example, Services X, Y, and Identity.
+    // X.initializeResources -> Y.requestCallback at X.address
+    // Y.requestCallback -> Identity.requestPermission to call X.address
+    // Therefore Identity must know of the permittable group for X.address before X.initializeResources is called.
+    final Stream<EventExpectation> eventExpectations = applicationNameToUriPairs.stream().flatMap(x ->
+            identityServiceInitializer.postApplicationPermittableGroups(
+                    tenantIdentifier,
+                    identityManagerApplicationName,
+                    identityManagerApplicationUri,
+                    x.uri).stream());
+    for (final EventExpectation eventExpectation : eventExpectations.collect(Collectors.toList())) {
+      if (!eventExpectation.waitForOccurrence(5, TimeUnit.SECONDS)) {
+        logger.warn("Expected action in identity didn't complete {}.", eventExpectation);
+      }
+    }
+
+
     applicationNameToUriPairs.forEach(x -> {
       final ApplicationSignatureSet applicationSignatureSet = anubisInitializer.initializeAnubis(
-              tenantEntity.getIdentifier(),
+              tenantIdentifier,
               x.name,
               x.uri,
               identityManagerSignatureSet.getTimestamp(),
               identityManagerSignatureSet.getIdentityManagerSignature());
 
       identityServiceInitializer.postApplicationDetails(
-              tenantEntity.getIdentifier(),
-              tenantEntity.getIdentityManagerApplicationName(),
-              tenantEntity.getIdentityManagerApplicationUri(),
+              tenantIdentifier,
+              identityManagerApplicationName,
+              identityManagerApplicationUri,
               x.name,
               x.uri,
               applicationSignatureSet);
diff --git a/service/src/main/java/io/mifos/provisioner/internal/service/applications/IdentityServiceInitializer.java b/service/src/main/java/io/mifos/provisioner/internal/service/applications/IdentityServiceInitializer.java
index a2b24db..bca9b4e 100644
--- a/service/src/main/java/io/mifos/provisioner/internal/service/applications/IdentityServiceInitializer.java
+++ b/service/src/main/java/io/mifos/provisioner/internal/service/applications/IdentityServiceInitializer.java
@@ -30,6 +30,8 @@ import io.mifos.identity.api.v1.domain.PermittableGroup;
 import io.mifos.permittedfeignclient.api.v1.client.ApplicationPermissionRequirements;
 import io.mifos.permittedfeignclient.api.v1.domain.ApplicationPermission;
 import io.mifos.provisioner.config.ProvisionerConstants;
+import io.mifos.provisioner.internal.listener.EventExpectation;
+import io.mifos.provisioner.internal.listener.IdentityListener;
 import io.mifos.tool.crypto.HashGenerator;
 import org.apache.commons.lang.RandomStringUtils;
 import org.slf4j.Logger;
@@ -41,6 +43,7 @@ import org.springframework.util.Base64Utils;
 
 import javax.annotation.Nonnull;
 import java.util.*;
+import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -49,7 +52,7 @@ import java.util.stream.Stream;
  */
 @Component
 public class IdentityServiceInitializer {
-
+  private final IdentityListener identityListener;
   private final ApplicationCallContextProvider applicationCallContextProvider;
   private final HashGenerator hashGenerator;
   private final Logger logger;
@@ -83,9 +86,11 @@ public class IdentityServiceInitializer {
 
   @Autowired
   public IdentityServiceInitializer(
+          final IdentityListener identityListener,
           final ApplicationCallContextProvider applicationCallContextProvider,
           final HashGenerator hashGenerator,
           @Qualifier(ProvisionerConstants.LOGGER_NAME) final Logger logger) {
+    this.identityListener = identityListener;
     this.applicationCallContextProvider = applicationCallContextProvider;
     this.hashGenerator = hashGenerator;
     this.logger = logger;
@@ -128,18 +133,42 @@ public class IdentityServiceInitializer {
     }
   }
 
+  public List<EventExpectation> postApplicationPermittableGroups(
+          final @Nonnull String tenantIdentifier,
+          final @Nonnull String identityManagerApplicationName,
+          final @Nonnull String identityManagerApplicationUri,
+          final @Nonnull String applicationUri) {
+    final List<PermittableEndpoint> permittables;
+    try (final AutoCloseable ignored = applicationCallContextProvider.getApplicationCallGuestContext(tenantIdentifier)) {
+      permittables = getPermittables(applicationUri);
+    } catch (final Exception e) {
+      throw new IllegalStateException(e);
+    }
+
+    try (final AutoCloseable ignored
+                 = applicationCallContextProvider.getApplicationCallContext(tenantIdentifier, identityManagerApplicationName)) {
+      final IdentityManager identityService = applicationCallContextProvider.getApplication(IdentityManager.class, identityManagerApplicationUri);
+
+      final Stream<PermittableGroup> permittableGroups = getPermittableGroups(permittables);
+      //You might look at this and wonder: "Why isn't she returning a stream here? She's just turning it back into
+      //a stream on the other side..."
+      //The answer is that you need the createOrFindPermittableGroup to be executed in the proper tenant context. If you
+      //return the stream, the call to createOrFindPermittableGroup will be executed when the stream is itereated over.
+      return permittableGroups.map(x -> createOrFindPermittableGroup(identityService, x)).collect(Collectors.toList());
+    } catch (final Exception e) {
+      throw new IllegalStateException(e);
+    }
+  }
+
   public void postApplicationDetails(
           final @Nonnull String tenantIdentifier,
           final @Nonnull String identityManagerApplicationName,
           final @Nonnull String identityManagerApplicationUri,
           final @Nonnull String applicationName,
           final @Nonnull String applicationUri,
-          final @Nonnull ApplicationSignatureSet applicationSignatureSet)
-  {
-    final List<PermittableEndpoint> permittables;
+          final @Nonnull ApplicationSignatureSet applicationSignatureSet) {
     final List<ApplicationPermission> applicationPermissionRequirements;
     try (final AutoCloseable ignored = applicationCallContextProvider.getApplicationCallGuestContext(tenantIdentifier)) {
-      permittables = getPermittables(applicationUri);
       applicationPermissionRequirements = getApplicationPermissionRequirements(applicationName, applicationUri);
 
     } catch (final Exception e) {
@@ -150,11 +179,11 @@ public class IdentityServiceInitializer {
                  = applicationCallContextProvider.getApplicationCallContext(tenantIdentifier, identityManagerApplicationName))
     {
       final IdentityManager identityService = applicationCallContextProvider.getApplication(IdentityManager.class, identityManagerApplicationUri);
+      final EventExpectation eventExpectation = identityListener.expectApplicationSignatureSet(tenantIdentifier, applicationName, applicationSignatureSet.getTimestamp());
       identityService.setApplicationSignature(applicationName, applicationSignatureSet.getTimestamp(), applicationSignatureSet.getApplicationSignature());
-      //TODO: I need to know when this is done.  ActiveMQ.  sigh.
-
-      final Stream<PermittableGroup> permittableGroups = getPermittableGroups(permittables);
-      permittableGroups.forEach(x -> createOrFindPermittableGroup(identityService, x));
+      if (!eventExpectation.waitForOccurrence(5, TimeUnit.SECONDS)) {
+        logger.warn("Expected action in identity didn't complete {}.", eventExpectation);
+      }
 
       applicationPermissionRequirements.forEach(x -> createOrFindApplicationPermission(identityService, applicationName, x));
 
@@ -218,32 +247,37 @@ public class IdentityServiceInitializer {
     });
   }
 
-  void createOrFindPermittableGroup(
+  EventExpectation createOrFindPermittableGroup(
           final @Nonnull IdentityManager identityService,
           final @Nonnull PermittableGroup permittableGroup) {
+    final EventExpectation eventExpectation = identityListener.expectPermittableGroupCreation(TenantContextHolder.checkedGetIdentifier(), permittableGroup.getIdentifier());
     try {
       identityService.createPermittableGroup(permittableGroup);
-      logger.info("Group '{}' successfully created in identity service for tenant {}.", permittableGroup.getIdentifier(), TenantContextHolder.checkedGetIdentifier());
+      logger.info("Group '{}' creation successfully requested in identity service for tenant {}.", permittableGroup.getIdentifier(), TenantContextHolder.checkedGetIdentifier());
     }
     catch (final PermittableGroupAlreadyExistsException groupAlreadyExistsException)
     {
       //if the group already exists, read out and compare.  If the group is the same, there is nothing left to do.
       final PermittableGroup existingGroup = identityService.getPermittableGroup(permittableGroup.getIdentifier());
       if (!existingGroup.getIdentifier().equals(permittableGroup.getIdentifier())) {
-        logger.error("Group '{}' already exists, but has a different name {} (strange).", permittableGroup.getIdentifier(), existingGroup.getIdentifier());
+        logger.error("Group '{}' already exists for tenant {}, but has a different name {} (strange).", permittableGroup.getIdentifier(), TenantContextHolder.checkedGetIdentifier(), existingGroup.getIdentifier());
+        identityListener.withdrawExpectation(eventExpectation);
       }
 
       //Compare as sets because I'm not going to get into a hissy fit over order.
       final Set<PermittableEndpoint> existingGroupPermittables = new HashSet<>(existingGroup.getPermittables());
       final Set<PermittableEndpoint> newGroupPermittables = new HashSet<>(permittableGroup.getPermittables());
       if (!existingGroupPermittables.equals(newGroupPermittables)) {
-        logger.error("Group '{}' already exists, but has different contents.", permittableGroup.getIdentifier());
+        logger.error("Group '{}' already exists for tenant {}, but has different contents.", permittableGroup.getIdentifier(), TenantContextHolder.checkedGetIdentifier());
+        identityListener.withdrawExpectation(eventExpectation);
       }
     }
     catch (final RuntimeException unexpected)
     {
-      logger.error("Creating group '{}' failed.", permittableGroup.getIdentifier(), unexpected);
+      logger.error("Creating group '{}' for tenant {} failed.", permittableGroup.getIdentifier(), TenantContextHolder.checkedGetIdentifier(), unexpected);
+      identityListener.withdrawExpectation(eventExpectation);
     }
+    return eventExpectation;
   }
 
   private void createOrFindApplicationPermission(
diff --git a/service/src/test/java/io/mifos/provisioner/internal/service/applications/IdentityServiceInitializerTest.java b/service/src/test/java/io/mifos/provisioner/internal/service/applications/IdentityServiceInitializerTest.java
index ebe6e18..9644fbf 100644
--- a/service/src/test/java/io/mifos/provisioner/internal/service/applications/IdentityServiceInitializerTest.java
+++ b/service/src/test/java/io/mifos/provisioner/internal/service/applications/IdentityServiceInitializerTest.java
@@ -17,9 +17,11 @@ package io.mifos.provisioner.internal.service.applications;
 
 import io.mifos.anubis.api.v1.client.Anubis;
 import io.mifos.anubis.api.v1.domain.PermittableEndpoint;
+import io.mifos.core.lang.AutoTenantContext;
 import io.mifos.identity.api.v1.client.IdentityManager;
 import io.mifos.identity.api.v1.client.PermittableGroupAlreadyExistsException;
 import io.mifos.identity.api.v1.domain.PermittableGroup;
+import io.mifos.provisioner.internal.listener.IdentityListener;
 import org.junit.Assert;
 import org.junit.Test;
 import org.mockito.Mockito;
@@ -56,6 +58,7 @@ public class IdentityServiceInitializerTest {
 
   @Test
   public void getPermittablesAnubisCallFails() throws Exception {
+    final IdentityListener identityListenerMock = Mockito.mock(IdentityListener.class);
     final ApplicationCallContextProvider applicationCallContextProviderMock = Mockito.mock(ApplicationCallContextProvider.class);
     final Logger loggerMock = Mockito.mock(Logger.class);
     final Anubis anubisMock = Mockito.mock(Anubis.class);
@@ -64,7 +67,7 @@ public class IdentityServiceInitializerTest {
     //noinspection unchecked
     when(anubisMock.getPermittableEndpoints()).thenThrow(IllegalStateException.class);
 
-    final List<PermittableEndpoint> ret = new IdentityServiceInitializer(applicationCallContextProviderMock, null, loggerMock)
+    final List<PermittableEndpoint> ret = new IdentityServiceInitializer(identityListenerMock, applicationCallContextProviderMock, null, loggerMock)
             .getPermittables("blah");
 
     Assert.assertEquals(ret, Collections.emptyList());
@@ -89,39 +92,48 @@ public class IdentityServiceInitializerTest {
 
   @Test
   public void createOrFindPermittableGroupThatAlreadyExists() throws Exception {
+    final IdentityListener identityListenerMock = Mockito.mock(IdentityListener.class);
     final Logger loggerMock = Mockito.mock(Logger.class);
 
     final IdentityManager identityServiceMock = Mockito.mock(IdentityManager.class);
     doThrow(PermittableGroupAlreadyExistsException.class).when(identityServiceMock).createPermittableGroup(group1);
     doReturn(reorderedGroup1).when(identityServiceMock).getPermittableGroup(group1.getIdentifier());
 
-    new IdentityServiceInitializer(null, null, loggerMock).createOrFindPermittableGroup(identityServiceMock, group1);
+    try (final AutoTenantContext ignored = new AutoTenantContext("blah")) {
+      new IdentityServiceInitializer(identityListenerMock, null, null, loggerMock).createOrFindPermittableGroup(identityServiceMock, group1);
+    }
   }
 
   @Test
   public void createOrFindPermittableGroupThatAlreadyExistsDifferently() throws Exception {
+    final IdentityListener identityListenerMock = Mockito.mock(IdentityListener.class);
     final Logger loggerMock = Mockito.mock(Logger.class);
 
     final IdentityManager identityServiceMock = Mockito.mock(IdentityManager.class);
     doThrow(PermittableGroupAlreadyExistsException.class).when(identityServiceMock).createPermittableGroup(group1);
     doReturn(changedGroup1).when(identityServiceMock).getPermittableGroup(group1.getIdentifier());
 
-    new IdentityServiceInitializer(null, null, loggerMock).createOrFindPermittableGroup(identityServiceMock, group1);
+    try (final AutoTenantContext ignored = new AutoTenantContext("blah")) {
+      new IdentityServiceInitializer(identityListenerMock, null, null, loggerMock).createOrFindPermittableGroup(identityServiceMock, group1);
+    }
 
-    verify(loggerMock).error(anyString(), anyString());
+    verify(loggerMock).error(anyString(), anyString(), anyString());
   }
 
   @Test
   public void createOrFindPermittableGroupWhenIsisCallFails() throws Exception {
+    final IdentityListener identityListenerMock = Mockito.mock(IdentityListener.class);
     final Logger loggerMock = Mockito.mock(Logger.class);
 
     final IdentityManager identityServiceMock = Mockito.mock(IdentityManager.class);
     doThrow(IllegalStateException.class).when(identityServiceMock).createPermittableGroup(group1);
     doReturn(changedGroup1).when(identityServiceMock).getPermittableGroup(group1.getIdentifier());
 
-    new IdentityServiceInitializer(null, null, loggerMock).createOrFindPermittableGroup(identityServiceMock, group1);
+    try (final AutoTenantContext ignored = new AutoTenantContext("blah")) {
+      new IdentityServiceInitializer(identityListenerMock, null, null, loggerMock).createOrFindPermittableGroup(identityServiceMock, group1);
+    }
 
-    verify(loggerMock).error(anyString(), anyString(), isA(IllegalStateException.class));
+    verify(loggerMock).error(anyString(), anyString(), anyString(), isA(IllegalStateException.class));
   }
 
 
diff --git a/shared.gradle b/shared.gradle
index 77669da..e4f5378 100644
--- a/shared.gradle
+++ b/shared.gradle
@@ -12,6 +12,8 @@ ext.versions = [
         frameworkmariadb     : '0.1.0-BUILD-SNAPSHOT',
         frameworkcrypto      : '0.1.0-BUILD-SNAPSHOT',
         frameworktest        : '0.1.0-BUILD-SNAPSHOT',
+        springcontext        : '4.3.3.RELEASE',
+        activeMQ             : '5.13.2',
         validator            : '5.3.0.Final'
 ]
 

-- 
To stop receiving notification emails like this one, please contact
myrle@apache.org.

Mime
View raw message