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 <mkra...@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
my...@apache.org.

Reply via email to