Skip to content

Commit

Permalink
Add descriptive comments and a thread sleep in child org template mig…
Browse files Browse the repository at this point in the history
…ration. Check primary org level before starting child org template migration.
  • Loading branch information
dhaura committed Dec 10, 2024
1 parent 74f0095 commit ba7fc9b
Showing 1 changed file with 49 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,22 @@
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.email.mgt.internal.I18nMgtDataHolder;
import org.wso2.carbon.identity.application.common.IdentityApplicationManagementException;
import org.wso2.carbon.identity.application.mgt.ApplicationManagementService;
import org.wso2.carbon.identity.core.ThreadLocalAwareExecutors;
import org.wso2.carbon.identity.governance.exceptions.notiification.NotificationTemplateManagerServerException;
import org.wso2.carbon.identity.governance.model.NotificationTemplate;
import org.wso2.carbon.identity.organization.management.service.OrganizationManager;
import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementException;
import org.wso2.carbon.identity.organization.management.service.model.BasicOrganization;
import org.wso2.carbon.identity.organization.management.service.util.Utils;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Base64;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;

/**
* This class is responsible for on-demand migrating notification templates from the registry to the database.
Expand Down Expand Up @@ -167,13 +163,31 @@ public void addOrUpdateNotificationTemplate(NotificationTemplate notificationTem
notificationChannel, displayName, locale, tenantDomain));
}

try {
OrganizationManager organizationManager = I18nMgtDataHolder.getInstance().getOrganizationManager();
String orgId = organizationManager.resolveOrganizationId(tenantDomain);
int depthInHierarchy = organizationManager.getOrganizationDepthInHierarchy(orgId);
int primaryOrgLevel = Utils.getSubOrgStartLevel() - 1;

/* If organization resides above the primary organization level, no need to migrate templates in
child organizations. */
if (depthInHierarchy < primaryOrgLevel) {
return;
}
} catch (OrganizationManagementException e) {
log.error("Error occurred while retrieving organization details for tenant: " + tenantDomain, e);
}

String usernameInContext = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername();
/* Since no default templates were stored for application notification templates, all the app templates
created in the registry are manually created by users. Additionally, application templates did not
support fallback (inheritance) mechanisms previously. Therefore, child organization template migration
will only be applied to organization-level templates.
*/
if (StringUtils.isBlank(applicationUuid)) {
/* Asynchronously migrate child organization templates of the given parent template to the
database in the background. This ensures the migration process does not block the main thread, as
it may take considerable time to traverse and process all child organizations in the hierarchy. */
CompletableFuture.runAsync(
() -> migrateChildOrgTemplates(registryBasedTemplate, tenantDomain, usernameInContext),
executorService);
Expand Down Expand Up @@ -308,6 +322,20 @@ private <T> List<T> mergeAndRemoveDuplicates(List<T> list1, List<T> list2) {
return new ArrayList<>(uniqueElements);
}

/**
* Migrates child organization notification templates of the given parent template to the database.
* <p>
* This method traverses the child organization hierarchy using a depth-first approach, retrieving child
* organizations in batches of 100. It migrates templates associated with child organizations to a database,
* ensuring that only updated or non-migrated templates are migrated. If a child organization does not have a
* template matching the parent template's attributes (display name, locale, notification channel), its subtree
* is skipped. After migration, the templates are removed from the registry.
* </p>
*
* @param parentTemplate The parent notification template used as a reference for migration.
* @param parentTenantDomain The tenant domain of the parent template.
* @param usernameInContext The username in the context of the parent template migration.
*/
private void migrateChildOrgTemplates(NotificationTemplate parentTemplate, String parentTenantDomain,
String usernameInContext) {

Expand All @@ -325,6 +353,8 @@ private void migrateChildOrgTemplates(NotificationTemplate parentTemplate, Strin
OrganizationManager organizationManager = I18nMgtDataHolder.getInstance().getOrganizationManager();
do {
try {
/* Retrieve immediate child organizations of the parent organization in batches of 100,
and migrate templates within child organizations using a depth-first traversal approach. */
List<BasicOrganization> childOrganizations =
organizationManager.getOrganizations(pageSize, cursor, null, "DESC", "", false);
for (BasicOrganization childOrganization : childOrganizations) {
Expand All @@ -333,10 +363,17 @@ private void migrateChildOrgTemplates(NotificationTemplate parentTemplate, Strin
NotificationTemplate childNotificationTemplate =
registryBasedTemplateManager.getNotificationTemplate(
displayName, locale, notificationChannel, null, childTenantDomain);
/* If the child organization does not have a template with the same display name, locale, and
notification channel in the registry, then skip template migration in the organization
subtree rooting from the given child organization. This organization subtree is already
migrated or a new organization subtree that doesn't need migration. */
if (childNotificationTemplate == null) {
continue;
}

/* If the child organization's template is not identical to parent's resolved template, and if
the template is not already migrated to the database, migrate the template
to the database. */
if (!areNotificationTemplatesEqual(parentTemplate, childNotificationTemplate) &&
!dbBasedTemplateManager.isNotificationTemplateExists(displayName, locale,
notificationChannel, null, childTenantDomain)) {
Expand All @@ -346,16 +383,23 @@ private void migrateChildOrgTemplates(NotificationTemplate parentTemplate, Strin

registryBasedTemplateManager.deleteNotificationTemplates(displayName, notificationChannel,
null, childTenantDomain);
/* According to the depth-first traversal approach, migrate child organization templates of
the given child organization. */
migrateChildOrgTemplates(childNotificationTemplate, childTenantDomain, usernameInContext);
}
cursor = getNextCursor(childOrganizations);
// Sleep the thread to avoid overloading the system.
Thread.sleep(100);
} catch (OrganizationManagementException e) {
log.error("Error occurred while retrieving child organizations of organization " +
"with tenant domain: " + parentTenantDomain, e);
} catch (NotificationTemplateManagerServerException e) {
log.error("Error occurred while migrating child organization templates of parent template: " +
displayName + " for locale: " + locale + " and notification channel: " +
notificationChannel, e);
} catch (InterruptedException e) {
log.error("Error occurred while sleeping the thread when migrating child organization " +
"templates for tenant: " + parentTenantDomain, e);
}
} while (cursor != null);
} finally {
Expand Down

0 comments on commit ba7fc9b

Please sign in to comment.